Добавление необходимых функций в библиотеку Общие функции (Common Functions).
Для возможности работы в качестве Modbus Slave устройства добавьте следующий код в библиотеку Скрипты –> Общие функции (Scripting -> Common Functions).
-- modbus proxy
mbproxy = {
-- supported function list
functions = {
'readdo',
'readcoils',
'readdi',
'readdiscreteinputs',
'readao',
'readregisters',
'readai',
'readinputregisters',
'writebits',
'writemultiplebits',
'writeregisters',
'writemultipleregisters',
'reportslaveid',
'getcoils',
'getdiscreteinputs',
'getinputregisters',
'getregisters',
'setcoils',
'setdiscreteinputs',
'setinputregisters',
'setregisters',
},
-- new connecton init
new = function()
require('rpc')
local mb = setmetatable({}, { __index = mbproxy })
mb.slaveid = 0
mb.rpc = rpc.client('127.0.0.1', 28002, 'mbproxy')
for _, fn in ipairs(mbproxy.functions) do
mb[ fn ] = function(self, ...)
return mb:request(fn, ...)
end
end
return mb
end
}
-- set local slave id
function mbproxy:setslave(slaveid)
self.slaveid = slaveid
end
-- send rpc request for a spefic function
function mbproxy:request(fn, ...)
local res, err = self.rpc:request({
fn = fn,
params = { ... },
slaveid = self.slaveid or 0,
})
-- request error
if err then
return nil, err
-- request ok
else
-- reply with an error
if res[ 1 ] == nil then
return nil, res[ 2 ]
-- normal reply
else
return unpack(res)
end
end
end
Modbus handler
Данная программа будет поддерживать работу Modbus и производить ответы на запросы
от Modbus Master устройства.
Состав скрипта:
1. mb:open(‘/dev/RS485-2’, 38400, ‘E’, 8, 1, ‘H’)
Открытие порта и установка параметров соединения. Для LogicMachine 2 порт RS485 – ttyS2,
для семейства LogicMachine 3 – RS485-#, где # — порядковый номер порта RS485 слева направо
2. mb:setslave(10)
Установка адреса LogicMachine в Modbus
3. mb:setmapping(10, 10, 10, 10)
Резервирование количества coils, discrete inputs, holding registers и input registers
4. mb:setwritecoilcb(function(coil, value)…
Функция обратного вызова, который выполняется для каждой записи в coil
5. mb:setwriteregistercb(function(coil, value)…
Функция обратного вызова, который выполняется для каждой записи в регистр
Рассмотрим на примере
Переходим в меню LogicMachine -> Скрипты -> Резидентные (LogicMachine -> Scripting -> Resident) и создаем новый резидентный скрипт с параметром Интервал запуска (Sleep interval) равным 0.
Нажмем на иконку редактирования скрипта для добавления следующего программного кода:
-- инициализация modbus
if not mb then
require('luamodbus')
mb = luamodbus.rtu()
--указать верный порт
mb:open('/dev/RS485-2', 9600, 'E', 8, 1, 'H')
mb:connect()
-- установка slave id
mb:setslave(10)
-- инициализация резервирования для coils, discrete inputs,
--holding registers и input registers
mb:setmapping(10, 10, 10, 10)
-- запись от мастера значения coil
mb:setwritecoilcb(function(coil, value)
if coil == 0 then
grp.write('3/2/75', value, dt.bool)
else
alert('coil: %d = %s', coil, tostring(value))
end
end)
-- запись от мастера значения register
mb:setwriteregistercb(function(register, value)
if register == 0 then
-- запись значения отгрниченного в пределах 0..100
grp.write('3/2/76', math.min(100, value), dt.scale)
else
alert('register: %d = %d', register, value)
end
end)
end
-- инициализация серверной части
if not server then
require('rpc')
-- incoming data handler
local handler = function(request)
local fn, res
fn = tostring(request.fn)
if not mb[ fn ] then
return { nil, 'unknown function ' .. fn }
end
if type(request.params) == 'table' then
table.insert(request.params, 1, mb)
res = { mb[ fn ](unpack(request.params)) }
else
res = { mb[ fn ](mb) }
end
return res
end
server = rpc.server('127.0.0.1', 28002, 'mbproxy', handler, 0.01)
end
mb:handleslave()
server:step()
Запись значений Modbus в мастер-устройство
Рассмотрим установление coil на примере скрипта по событию. Скрипт отвечает на запись в KNX адрес 1 битового значения и записывает в coil с адресом 2.
Переходим в меню LogicMachine -> Скрипты -> Событийные (LogicMachine -> Scripting -> Event-based) и создаем новый скрипт по событию.
value = event.getvalue()
mb = mbproxy.new()
mb:setcoils(2, value)
Рассмотрим аналогичную ситуацию, но с записью в регистр. Записываем байтовое значение в регистр 5. Далее аналогично предыдущему пункту.
value = event.getvalue()
mb = mbproxy.new()
mb:setregisters(5, value)