User Tools

Site Tools


modbus_tcp_custom

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