Шаг 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')