====== An example of custom protocol for ModBus TCP ====== ***Type**: TCP ***Default TCP port**: 502 ***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 TCP Demo Driver function createDevices () 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, 16}}); addDevice({name = "IR", shift = 0, base = 10, xtraFields = {4, 0}}); end local transId = 0; local errorCount = 0; function readRegister (reg, device, unitId) local request = {}; -- transaction ID transId = transId + 1; request[1] = bit.band(bit.rshift(transId, 8), 255); request[2] = bit.band(transId, 255); -- protocol ID request[3] = 0; request[4] = 0; -- message length request[5] = 0; request[6] = 6; -- unit ID request[7] = unitId; -- function code request[8] = device.xtraFields[1]; -- address of register request[9] = bit.band(bit.rshift(reg.internalAddr, 8), 255); request[10] = bit.band(reg.internalAddr, 255); -- count of registers request[11] = 0; request[12] = 1; if (reg.dataType == 3) then -- double word request[12] = 2; end local res = sendBytes(request); if (res == false) then DEBUG("Can't send bytes"); return false; end local response = {}; -- read MBAP Header response = readBytes(7); if (response == false) then DEBUG("Can't read response"); return false; end res = #response; if (res ~= 7) then errorCount = errorCount + 1; if (errorCount > 3) then closeConnection(); errorCount = 0; end DEBUG("Can't read MBAP"); return false; end if (response[1] ~= request[1] or response[2] ~= request[2]) then ERROR("Wrong transaction ID. Got #" .. (response[1] * 256 + response[2]) .. " but expected #" .. (request[1] * 256 + request[2])); return false; end if (response[3] ~= request[3] or response[4] ~= request[4]) then ERROR("Wrong protocol"); return false; end if (response[7] ~= request[7]) then ERROR("Wrong UnitID in response"); return false; end local length = response[5] * 256 + response[6]; if (length < 1) then ERROR("Wrong length in response"); return false; end local responsePDU = {}; -- read MBAP Header responsePDU = readBytes(length - 1); if (responsePDU == false) then DEBUG("Can't read responsePDU"); return false; end res = #responsePDU; if (responsePDU[1] ~= request[8]) then ERROR("Wrong function in response"); return false; end local dataLength = responsePDU[2]; if (dataLength ~= length - 3) then ERROR("Wrong length in PDU"); return false; end local result = {}; if (dataLength >= 1) then for i = 1, dataLength do result[i] = responsePDU[2 + i]; end end return result; end function writeRegister (reg, device, unitId, newValue) local request = {}; transId = transId + 1; -- transaction ID request[1] = bit.band(bit.rshift(transId, 8), 255); request[2] = bit.band(transId, 255); -- protocol ID request[3] = 0; request[4] = 0; if (reg.dataType == 3) then -- double word -- message length request[5] = 0; request[6] = 11; -- unit ID request[7] = unitId; -- function code request[8] = device.xtraFields[3]; -- address of register request[9] = bit.band(bit.rshift(reg.internalAddr, 8), 255); request[10] = bit.band(reg.internalAddr, 255); -- count of registers request[11] = 0; request[12] = 2; -- bytes with data request[13] = 4; -- value of registers request[14] = bit.band(bit.rshift(newValue, 24), 255); request[15] = bit.band(bit.rshift(newValue, 16), 255); request[16] = bit.band(bit.rshift(newValue, 8), 255); request[17] = bit.band(newValue, 255); local res = sendBytes(request); if (res == false) then DEBUG("Can't send bytes"); return false; end local response = {}; response = readBytes(7); if (response == false) then DEBUG("Can't read response"); return false; end res = #response; if (res ~= 7) then DEBUG("Wrong response length"); return false; end if (response[1] ~= request[1] or response[2] ~= request[2]) then ERROR("Wrong transaction ID. Got #" .. (response[1] * 256 + response[2]) .. " but expected #" .. (request[1] * 256 + request[2])); return false; end if (response[3] ~= request[3] or response[4] ~= request[4]) then ERROR("Wrong protocol"); return false; end if (response[7] ~= request[7]) then ERROR("Wrong UnitID in response"); return false; end local length = response[5] * 256 + response[6]; if (length < 1) then ERROR("Wrong length in response"); return false; end local responsePDU = {}; responsePDU = readBytes(length - 1); if (responsePDU == false) then DEBUG("Can't read responsePDU"); return false; end res = #responsePDU; if (responsePDU[1] ~= request[8]) then ERROR("Wrong function in response"); return false; end if (responsePDU[2] ~= request[9] or responsePDU[3] ~= request[10]) then ERROR("Wrong register address in response"); return false; end if (responsePDU[4] ~= 0 or responsePDU[5] ~= 2) then ERROR("Wrong register count in response"); return false; end else if (device.xtraFields[2] == 0) then ERROR("Can't write these type of registers (" .. device.name .. ")"); return false; end -- message length request[5] = 0; request[6] = 6; -- unit ID request[7] = unitId; request[8] = device.xtraFields[2]; -- address of register request[9] = bit.band(bit.rshift(reg.internalAddr, 8), 255); request[10] = bit.band(reg.internalAddr, 255); local val = newValue; if (reg.dataType == 0) then if (val > 0) then val = 255*256; else val = 0; end end -- value of registers request[11] = bit.band(bit.rshift(val, 8), 255); request[12] = bit.band(val, 255); local res = sendBytes(request); if (res == false) then DEBUG("Can't send bytes"); return false; end local response = {}; local requestLen = #request; response = readBytes(requestLen); if (response == false) then DEBUG("Can't read response"); return false; end res = #response; if (res ~= requestLen) then DEBUG("Wrong response length"); return false; end for i = 1,res do if (response[i] ~= request[i]) then DEBUG("Wrong response"); return false; end end end return true; end