User Tools

Site Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

modbus_rtu_custom [2019/01/09 12:18] (current)
emozolyak created
Line 1: Line 1:
 +
 +
 +===== Modbus RTU in custom protocol version =====
 +
 +An example of custom protocol for Modbus RTU.
 +
 +  *Type: Serial Port
 +  *Address validation: ^(C[0-9]+)$|^(DI[0-9]+)$|^(HR[0-9]+)$|^(IR[0-9]+)$
 +  *Validation error message: Invalid register address. Valid ModBus addresses are Cxxx, DIxxx, IRxxx, HRxxx.
 +Code:
 +<code lua>
 +-- MODBUS RTU Demo Driver
 +
 +function createDevices ()
 +                                                     -- read FC write FC
 +  addDevice({name = "​C", ​ shift = 0, base = 10, xtraFields = {1, 5}})
 +  addDevice({name = "​DI",​ shift = 0, base = 10, xtraFields = {2, 0}})
 +  addDevice({name = "​HR",​ shift = 0, base = 10, xtraFields = {3, 6}})
 +  addDevice({name = "​IR",​ shift = 0, base = 10, xtraFields = {4, 0}})
 +  ​
 +end
 +
 +local errorCount = 0
 +
 +-- template ​
 +local request = {1, 2,      -- slaveId FC
 +                 ​3, ​ 4,  -- addr high lo 
 +                 ​5, ​ 6, -- count hi lo 
 +                 ​0, ​ 0      -- crc high lo 
 +                }     
 +
 +EXCEPTIONS = {"​Illegal Function", ​    "​Illegal Data Address",​
 +               "​Illegal Data Value", ​ "Slave Device Failure",​
 +               "​Acknowledge", ​        "​Slave Device Busy",
 +               "​Negative Acknowledge",​ "​Memory Parity Error",​
 +               "​Gateway Path Unavailable",​ "​Gateway Target Device Failed to Respond"​
 +              }
 +
 +function readRegister (reg, device, unitId)
 +                             --- FORMING REQUEST ----------- ​
 +      -- slave address
 +  request[1] = unitId;
 +  ​
 +  -- function code
 +  request[2] = device.xtraFields[1]
 +  ​
 +  -- address of register
 +  request[3] = GetHiByte(reg.internalAddr)
 +  request[4] = GetLoByte(reg.internalAddr)
 +  ​
 +  -- count of registers
 +  local count = 1
 +      if (reg.dataType == 3) then -- double word
 +        count = 2
 +      end
 +  request[5] = GetHiByte(count)
 +  request[6] = GetLoByte(count)
 +  ​
 +   -- CRC
 +  local crc = GetCRC(request,​ 2)
 +  local crcLo,crcHi = 0,0 -- will be used below too 
 +  crcLo = GetLoByte(crc) ; request[7] = crcLo
 +  crcHi = GetHiByte(crc) ; request[8] = crcHi
 +
 +                           -- SENDING REQUEST ​
 +  if not (sendBytes(request)) then
 +      DEBUG("​Can'​t send bytes"​)
 +      return false
 +  end
 +                           -- RECEIVING REPLY --- 
 +  local respHead, respData, respCRC = {}, {}, {}
 +  ​
 +    -- read Header with length ​
 +  respHead = readBytes(3)
 +  if (respHead == false) then
 +      DEBUG("​Can'​t read response"​)
 +      return false
 +  end
 +  ​
 +  if (respHead[1] ~= request[1]) then
 +      ERROR("​Wrong slaveID in response!"​)
 +      return false
 +  end
 +  ​
 +  if (respHead[2] ~= request[2]) then
 +      if (respHead[2] >= 0x81) then 
 +          ERROR("​EXCEPTION:​ "​..EXCEPTIONS[bit.band(respHead[2],​ 0x0F)])
 +          readBytes(2) -- read till the end 
 +      else       
 +          ERROR("​Wrong Func Code in response"​)
 +      end 
 +   ​return false;
 +  end
 +  ​
 +  local resp_Lentgh = respHead[3]; ​
 +        respData = readBytes(resp_Lentgh)
 +  if (respData == false) then
 +      DEBUG("​Can'​t read response"​);​
 +      return false;
 +  end
 +  -- check CRC in reply 
 +  local tmpResponseTab = {}
 +    for i,v in ipairs(respHead) do 
 +        table.insert(tmpResponseTab,​ v) 
 +    end 
 +    for i,v in ipairs(respData) do 
 +        table.insert(tmpResponseTab,​ v) 
 +    end 
 +  ​
 +  crc = GetCRC(tmpResponseTab,​ 0)
 +  crcLo = GetLoByte(crc)
 +  crcHi = GetHiByte(crc)
 +    ​
 +  respCRC = readBytes(2)
 +  if (respCRC == false) then
 +      DEBUG("​Can'​t read response"​);​
 +      return false;
 +  end
 +  if (respCRC[1] ~= crcLo) or (respCRC[2] ~= crcHi) then 
 +      DEBUG("​Wrong CRC in reply! "​..string.format("​%X",​ crcLo).."​ "​..string.format("​%X",​ crcHi));
 +      return false;
 +  end 
 +  ​
 +  if (device.name == "​DI"​) or (device.name == "​C"​) ​ then 
 +      if (resp_Lentgh ~= count) then
 +          ERROR("​Wrong length in response"​);​
 +          return false;
 +      end
 +  else 
 +      if (resp_Lentgh ~= count*2) then
 +          ERROR("​Wrong length in response"​);​
 +          return false;
 +      end
 +  end 
 +                                  -- RETURN DATA --  ​
 +    return GetHexFromTable(respData)
 +
 +end -- readRegister
 +
 +
 +
 +function writeRegister (reg, device, unitId, newValue)
 +    -- for read-only don't write 
 +                                    DEBUG("​Going to write this value "​..newValue)
 +    if device.name == "​IR"​ or device.name == "​DI"​ then 
 +         ​ERROR("​Can'​t write these type of registers (" .. device.name .. "​)"​)
 +        return true 
 +    end 
 +    local wrRequest = {};
 +    ​
 +    -- slave address
 +    wrRequest[1] = unitId; ​  
 +    ​
 +    -- function code
 +    wrRequest[2] = device.xtraFields[2]
 +    ​
 +    -- address of register
 +    wrRequest[3] = GetHiByte(reg.internalAddr)
 +    wrRequest[4] = GetLoByte(reg.internalAddr)
 +    ​
 +    local dataTable = GetDataAsTable(newValue,​ device.dataType)
 +        ​
 +        local coilsTmp = 0 
 +        if (device.name == "​C"​) then 
 +            coilsTmp = dataTable[2] * 0xFF 
 +            dataTable[2] = dataTable[1]
 +            dataTable[1] = coilsTmp
 +        end 
 +    DEBUG("#​dataTable now "​..#​dataTable)
 +    ​
 +    wrRequest[5] = dataTable[1]
 +    wrRequest[6] = dataTable[2]
 +    ​
 +    -- CRC
 +    local crc, crcLo, crcHi = 0,0,0
 +    crc = GetCRC(wrRequest,​ 0)
 +    crcLo = GetLoByte(crc)
 +    crcHi = GetHiByte(crc) ​
 +    wrRequest[7] = crcLo
 +    wrRequest[8] = crcHi 
 +    ​
 +    DEBUG("​Going to send this packet "​..table.concat(wrRequest)) ​   ​
 +    local res = sendBytes(wrRequest);​
 +    if (res == false) then
 +        DEBUG("​Can'​t send request"​);​
 +        return false;
 +    end
 +    ​
 +    -- читаем ответ ​
 +    res = readBytes(8) ​
 +    ​
 +    if (res == false) then
 +        DEBUG("​Can'​t receive reply"​)
 +        return false
 +    end 
 +    ​
 +    if (table.concat(res) ~= table.concat(wrRequest)) then 
 +        DEBUG("​Response does not match!"​)
 +        return false
 +    end 
 +    ​
 +    if (#dataTable == 4) then 
 +        -- DWORD
 +        -- repeat steps for 2nd Word 
 +        wrRequest[3] = GetHiByte(reg.internalAddr + 1)
 +        wrRequest[4] = GetLoByte(reg.internalAddr + 1)
 +        wrRequest[5] = dataTable[3]
 +        wrRequest[6] = dataTable[4]
 +        crc, crcLo, crcHi = GetCRC(wrRequest,​ 0), GetLoByte(crc) , GetHiByte(crc) ​
 +        wrRequest[7] = crcLo
 +        wrRequest[8] = crcHi 
 +        res = sendBytes(wrRequest);​
 +        if (res == false) then
 +            DEBUG("​Can'​t send request"​);​
 +            return false;
 +        end
 +    end 
 +    return true
 +end
 +
 +
 +-- Calculating CRC for RTU 
 +function GetCRC(req, offset) ​
 +    ​
 +  local crc = 0xffff
 +  local mask = 0
 +  ​
 +  -- iterate bytes
 +for i=1,#​req-offset do
 +      crc = bit.bxor(crc,​req[i])
 +      -- iterate bits in byte 
 +      for j=1,8 do
 +          mask = bit.band(crc,​0x0001)
 +          if mask == 0x0001 then
 +              crc = bit.rshift(crc,​1)
 +              crc = bit.bxor(crc,​0xA001)
 +          else
 +              crc = bit.rshift(crc,​1)
 +          end
 +          ​
 +      end 
 +end 
 +     ​return crc
 +end
 +
 +function GetHiByte(c)
 +                                   ​DEBUG("​Going to get high byte from "..c)
 +   ​return bit.rshift(c,​8)
 +end
 +
 +function GetLoByte(input_val) ​
 +                                    DEBUG("​Going to get low byte from "​..input_val)
 +    return bit.band(input_val,​0xFF)
 +end
 +
 +function GetHexFromTable(inputTab)
 +    -- get hex and concat it to number via string operatoin ​
 +                                    DEBUG("​entered GetHexFromTable with table - "​..table.concat(inputTab))
 +    local numberAs_String = "" ​
 +    local tmpStr = "" ​
 +    ​
 +        for i,v in pairs(inputTab) do 
 +            tmpStr = string.format("​%X",​v)
 +            if (#tmpStr == 1) then 
 +                tmpStr = "​0"​..tmpStr
 +            end 
 +            numberAs_String = numberAs_String..tmpStr
 +        end 
 +                                DEBUG("​number is "​..numberAs_String.."​ decimal = "​..numberAs_String) ​   ​
 +    return tonumber(numberAs_String,​ 16)
 +end
 +
 +function GetDataAsTable(value,​ datatype)
 + 
 +    local highWord, lowWord, tmpTable = 0, 0, {}
 +    ​
 +    if (datatype ~= 3) then 
 +        DEBUG("​getdataastable - going to process value  "​..value)
 +        tmpTable[1] = GetHiByte(value) ; DEBUG(tmpTable[1])
 +        tmpTable[2] = GetLoByte(value) ; DEBUG(tmpTable[2])
 +    else 
 +        highWord = bit.rshift(value,​16)
 +        lowWord = bit.band(c,​0xFFFF)
 +        ​
 +        tmpTable[1] = GetHiByte(highWord)
 +        tmpTable[2] = GetLoByte(highWord)
 +        tmpTable[3] = GetHiByte(lowWord)
 +        tmpTable[4] = GetLoByte(lowWord)
 +    end 
 +    return tmpTable
 +end
 +</​code>​
 +
  

Page Tools