Горячая линия Embedded System Rus:8-800-775-06-34 (звонок по России бесплатный)

LM5_N
LM-Wall_N
DALI_N
Vita_N

Управление DMX освещением при помощи LogicMachine

Шаг 1 – Подключение библиотеки DMX

Сначала необходимо подключить библиотеку DMX. Для этого в разделе Скрипты – > Пользовательские библиотеки (Scripts -> User libraries) нажмите Добавить библиотеку (Add new library) и создайте библиотеку с названием dmx.


Скопируйте в редактор библиотеки следующий код:

local luadmx = require("luadmx")
module("DMX", package.seeall)
 
local DMX = {}
 
-- default params
local defaults = {
    -- storage key
    skey = "dmx_line_1",
    -- RS-485 port
    port = "/dev/RS485",
    -- number of calls per second
    resolution = 20,
    -- total number of channels to use
    channels = 3,
    -- transition time in seconds, does not include DMX transfer time
    transition = 2
}
 
-- value setter
function set(chan, val, key)
    key = key or defaults.skey
    chan = tonumber(chan) or 0
    val = tonumber(val) or -1
 
    -- validate channel number and value
    if chan >= 1 and chan <= 512 and val >= 0 and val <= 255 then
        storage.exec("lset", key, chan - 1, val)
    end
end
 
-- value getter
function get(chan, key)
    local res, val
    key = key or defaults.skey
    chan = tonumber(chan) or 0
 
    -- validate channel number and value
    if chan >= 1 and chan <= 512 then
        res = storage.exec("lrange", key, chan - 1, chan - 1)
        if type(res) == "table" then
            val = tonumber(res[1])
        end
    end
 
    return val
end
 
-- DMX init, returns new DMX object
function init(params)
    local n, k, v, _
 
    -- create metatable and set user parameters
    n = setmetatable({}, {__index = DMX})
    n.params = params or {}
 
    _, n.conn = pcall(require("redis").connect)
 
    -- merge parameters that are set by user
    for k, v in pairs(defaults) do
        if n.params[k] == nil then
            n.params[k] = v
        end
    end
 
    n:reset()
 
    return n
end
 
function DMX:reset()
    local err, chan, params
 
    params = self.params
    self.dm, err = luadmx.open(params.port)
 
    -- error while opening
    if err then
        os.sleep(1)
        error(err)
    end
 
    -- set channel count
    self.dm:setcount(params.channels)
 
    -- number of transaction ticks
    self.ticks = math.max(1, params.transition * params.resolution)
 
    -- calculate sleep time
    self.sleep = 1 / params.resolution
 
    -- reset channel map
    self.channels = {}
 
    -- empty channel value map
    self.conn:ltrim(params.skey, 1, 0)
 
    -- fill channel map
    for chan = 1, params.channels do
        self.channels[chan] = {current = 0, target = 0, ticks = 0}
 
        -- turn off by default
        self.conn:lpush(params.skey, 0)
        self.dm:setchannel(chan, 0)
    end
end
 
-- get new values
function DMX:getvalues()
    local max, channels, ticks, values, val
 
    max = self.params.channels
    channels = self.channels
    ticks = self.ticks
    values = self.conn:lrange(self.params.skey, 0, max - 1) or {}
 
    -- check for new values for each channel
    for chan = 1, max do
        val = tonumber(values[chan]) or 0
 
        -- target value differs, set transcation
        if val ~= channels[chan].target then
            channels[chan].target = val
            channels[chan].delta = (channels[chan].target - channels[chan].current) / ticks
            channels[chan].ticks = ticks
        end
    end
end
 
-- main loop handler
function DMX:run()
    self:getvalues()
 
    -- transition loop
    for i = 1, self.params.resolution do
        self:step()
        self.dm:send()
        os.sleep(self.sleep)
    end
end
 
-- single transition step
function DMX:step()
    local chan, channels, t
 
    channels = self.channels
 
    -- transition for each channel
    for chan = 1, self.params.channels do
        t = channels[chan].ticks
 
        -- transition is active
        if t > 0 then
            t = t - 1
 
            channels[chan].current = channels[chan].target - channels[chan].delta * t
            channels[chan].ticks = t
 
            self.dm:setchannel(chan, channels[chan].current)
        end
    end
end

Шаг 2 – Создание DMX обработчика

Внутри резидентного сценария должен быть размещен DMX обработчик (handler). Значение интервала запуска должно быть выставлено равно «0».

if not dmxhandler then
  require('user.dmx')
  dmxhandler = DMX.init({
    -- количество каналов для использования
	channels = 3,
	-- время мягкого перехода в секундах
    transition = 2,
	-- название и номер порта RS-485
	port = '/dev/RS485-2', -- для LM2 используйте port = '/dev/ttyS2'
  })
end
 
dmxhandler:run()

Параметры (таблица)

  • channels (каналы) – (опционально, стандартное значение — 3) количество каналов для использования
  • resolution (разрешение) – (опционально, стандартное значение — 20) количество обновлений DMX в секунду. Большее значение дает более гладкие переходы при большем использовании CPU
  • transition (переход) – (опционально, стандартное значение — 2) время мягкого перехода в секундах
  • port (порт) – (опционально) название и номер порта RS-485.

Если в dmx handler не выставлены какие-либо значения параметров, то эти значения используются по умолчанию из библиотеки.

Шаг 3 – Создание резидентного или событийного сценария

Для того чтобы отправить определенное значение на канал DMX следует воспользоваться следующим кодом:

DMX.set(channel, value)

  • channel (канал) – значение DMX канала [1..512]
  • value (значение) – значение DMX канала [0..255]

Пример отправки определенного цвета:
Следующий пример должен быть размещен внутри резидентного сценария. При каждом срабатывании скрипта, будет получено значение, которое будет отправляться в первый канал.

-- объявление библиотеки dmx
require('user.dmx')
 
-- получение значение события (размерность - 1 байт)
value = event.getvalue()
 
-- конвертация [0..100] -> [0..255]
value = math.floor(value * 2.55)
 
-- отправление значения в первый канал
DMX.set(1, value)

Пример предопределенной сцены:
Следующий пример должен быть размещен внутри резидентного сценария.
Длительность проигрывания сцены можно менять через параметр «интервал запуска» скрипта (минимум 1 секунда).
—код dmx_scenes.lua

-- объявление библиотеки dmx
require('user.dmx')
 
if not scenes then
  -- сцена для 3х каналов
  scenes = {
    { 255, 0, 0 },
    { 0, 255, 0 },
    { 0, 0, 255 },
    { 255, 255, 0 },
    { 0, 255, 255 },
    { 255, 0, 255 },
    { 255, 255, 255 },
   }
  current = 1
end
 
-- установить значения текущей сцены
scene = scenes[ current ]
for i, v in ipairs(scene) do
   DMX.set(i, v)
end
 
-- переключить к следующей сцене
current = current + 1
if current > #scenes then
   current = 1
end

Пример случайной сцены:
Следующий пример должен быть размещен внутри резидентного сценария. Длительность проигрывания сцены можно менять через параметр «интервал запуска» скрипта (минимум 1 секунда).
— код dmx_rand.lua
-- объявление библиотеки dmx
require('user.dmx')
 
-- количество шагов, например 3 шага = { 0, 127, 255 }
steps = 5
-- количество каналов
channels = 3
-- номер первого канала
offset = 1
 
for i = offset, channels do
  v = math.random(0, (steps - 1)) * 255 / (steps - 1)
  DMX.set(i, math.floor(v))
end

Одновременное управление DMX по двум портам RS-485

LogicMachine может работать с несколькими линиями DMX. Для этого необходимо использовать модифицированный обработчик, который представлен ниже: 

 
if not dmxhandler thenrequire('user.dmx')dmxhandler = DMX.init({channels = 15, transition = 1, port = '/dev/RS485-1'})dmxhandler2 = DMX.init({skey = 'dmx_line_2', channels = 15, transition = 1port = '/dev/RS485-2'})]]end
dmxhandler:run()dmxhandler2:run()

Отправка в конкретную линию DMX происходит с использованием значения переменной skey, отвечающей за запись в storage. Поэтому, чтобы отправить команду во вторую линию, в представленном примере, необходимо использовать команду:  

DMX.set(1, 100, 'dmx_line2')

Если не задать параметр skey явно, в библиотеке ему присвоится стандартное значение (см. библиотеку dmx строка 9) и, соответственно, команды уйдут в первую DMX линию (порт первой линии см. библиотеку dmx строка 11). 
Пример одновременной работы с двумя линиями DMX представлен ниже:

DMX.set(1, 100) -- или DMX.set(1,100)DMX.set(1,100.'dmx_line2')