modbus_rtu_custom
This is an old revision of the document!
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:
-- 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
modbus_rtu_custom.1547036328.txt.gz · Last modified: 2019/01/09 12:18 by emozolyak