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

LM5_N
LM-Wall_N
DALI_N
Vita_N

Работа LogicMachine в качестве Modbus Slave устройства

Добавление необходимых функций в библиотеку Общие функции (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.

Modbus02

Нажмем на иконку редактирования скрипта для добавления следующего программного кода:

-- инициализация 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) и создаем новый скрипт по событию.

Modbus01

value = event.getvalue()
mb = mbproxy.new()
mb:setcoils(2, value)

Рассмотрим аналогичную ситуацию, но с записью в регистр. Записываем байтовое значение в регистр 5. Далее аналогично предыдущему пункту.
value = event.getvalue()
mb = mbproxy.new()
mb:setregisters(5, value)