Modbus Driver

Modbus is a data communications protocol originally published by Modicon (now Schneider Electric) in 1979 for use with its programmable logic controllers (PLCs). Modbus has become a de facto standard communication protocol and is now a commonly available means of connecting industrial electronic devices.

Modbus is popular in industrial environments because it is openly published and royalty-free. It was developed for industrial applications, is relatively easy to deploy and maintain compared to other standards, and places few restrictions on the format of the data to be transmitted.

Requirements

Since Modbus is a client/server (formerly master/slave) protocol, the client node must routinely poll each field device and look for changes in the data.

The Core Services of the Edge Compute Platform need to have been installed.

The following services need to be installed on the edge in order to use the Modbus Driver:
  • ase-modbus-driver

These services can be deployed by adding the "App - ECP Modbus Driver" application from the Marketplace to your own space (see "Edge Ops > Studio > Marketplace").

Figure 1.


Objective

The goal is to create a "Modbus device driver" to communicate with modbus server devices to control and gather telemetry. Only Modbus TCP/IP version is considered for implementation.

Details

Next object types provided by a Modbus server device to a Modbus client device:
  • Coil (Read-write, 1 bit)
  • Discrete input (Read-only, 1 bit)
  • Input register (Read-only, 16 bits)
  • Holding register (Read-write, 16 bits)

Each type has its own address space (0-0xFFFF). Server can have coil and input register with the same address. User should know available objects and their meaning for specific server. More complex data types (float, int32) can be built on top of registers and it is next level responsibility to handle/display it properly.

Thing Description

Example:
{
  "@type": [
    "swx:MBSlave"
  ],
  "id": "/things/mb_slave1",
  "title": "Modbus server #1",
  "properties": {
    "ip": {
      "title": "IP address",
      "description": "Device (slave) IP address",
      "type": "string",
      "readOnly": false
    },
    "port": {
      "title": "Port",
      "description": "Device (listening) port",
      "type": "integer",
      "minimum": 1,
      "maximum": 65525,
      "readOnly": false
    },
    "slaveid": {
      "title": "Slave Id",
      "description": "Id of slave device (may be omitted for TCP)",
      "type": "integer",
      "minimum": 1,
      "maximum": 65535,
      "readOnly": false
    },
    "pollperiod": {
      "title": "Poll Period",
      "description": "Data will be retrieved every <pollperiod> seconds, 0 - disabled",
      "type": "integer",
      "minimum": 0,
      "maximum": 7200,
      "readOnly": false
    },
    "input-status,1": {
      "title": "Input Conatct",
      "description": "Binary input (addr=1)",
      "type": "integer",
      "minimum": 0,
      "maximum": 1,
      "readOnly": true
    },
    "coil,1": {
      "title": "Output Coil",
      "description": "Binary output (addr=1)",
      "type": "integer",
      "minimum": 0,
      "maximum": 1,
      "readOnly": false
    },
    "input-register,33": {
      "title": "Input Register",
      "description": "Input register (addr=33)",
      "type": "integer",
      "minimum": 0,
      "maximum": 65535,
      "readOnly": true
    },
    "holding-register,44": {
      "title": "Output register",
      "description": "Output register (addr=44)",
      "type": "integer",
      "minimum": 0,
      "maximum": 65535,
      "readOnly": false
    }
  },
  "actions": {
    "readvalue": {
      "title": "Read Value",
      "description": "Reads input/output coil/register value at address <address>",
      "input": {
        "@type": "ValueAddress",
        "type": "object",
        "properties": {
          "address": {
            "type": "integer",
            "minimum": 0,
            "maximum": 65535
          },
          "type": {
            "type": "string"
          }
        }
      }
    }
  }
}

Important properties that define modbus server are: ip, port - IP address and port of modbus server. slaveid can be used if server requires it (mostly ignored in case of modbus TCP/IP).

pollinterval allows periodically poll data for each data object (global property, not per data object). If value is 0 - polling is disabled, the only way to get the value is readvalue action.

The rest of the properties represent modbus objects available on the server.

Property name looks like , (for example "coil,123" ).

Possible types are:
  • input-status - discrete input
  • coil - output coil
  • input-register
  • holding-register
  • address - any from 0-65535 range

Since data can be only acquired by polling, readvalue action is implemented for reading. Example of parameters (type, address limitations are the same as for property name):

Action input example:
{
  "readvalue": {
    "input": {
      "type": "coil",
      "address": 1
    }
  }
}