/WolkGatewayModule-Modbus

WolkGateway module for connecting Modbus devices to WolkAbout IoT Platform by communicating with WolkGateway.

Primary LanguageC++Apache License 2.0Apache-2.0

WolkGateway Module Modbus

WolkGateway module for connecting Modbus devices to WolkAbout IoT Platform by communicating with WolkGateway.

Supported protocol(s):

  • Wolkabout Protocol

Installing from source

This repository must be cloned from the command line using:

git clone --recurse-submodules https://github.com/Wolkabout/WolkGatewayModule-Modbus.git

Prerequisite

Following tools/libraries are required in order to build WolkGateway Module Modbus

  • cmake - version 3.5 or later
  • autotools
  • automake
  • autoconf
  • g++
  • m4
  • zlib1g-dev
  • libtool
  • openssl

Former can be installed on Debian based system from terminal by invoking:

sudo apt-get install automake g++ autotools-dev autoconf m4 zlib1g-dev cmake libtool libssl-dev

Afterwards dependencies are built, and Makefile build system is generated by invoking:

./configure

Generated build system is located inside out directory To build the module, invoke:

cd out
make -j$(nproc)
sudo make install

Installing will place the application in PATH, and create a systemctl unit, which you can use with:

sudo service modbus_module status/start/stop/restart

The configuration files used are placed in /etc/modbusModule/, which you should configure before you start your service. If you don't know how to configure the module, continue on to the next part.

Configuring Module

Module configuration consists of 2 configurations files

  • moduleConfiguration.json
  • devicesConfiguration.json

Below are sections describing each of these configuration files that need to be edited with the parameters of your modbus devices before running the application. These files are located in out directory, and are passed to Modbus module executable in following manner:

./modbusModule moduleConfiguration.json devicesConfiguration.json

moduleConfiguration.json

Module configuration file contains settings that relate to communication with WolkGateway, and outgoing Modbus connection, reading period time and response timeout.

{
  "mqttHost": "tcp://localhost:1883",
  // Address of local MQTT broker (connection with WolkGateway) 
  "connectionType": "SERIAL/RTU",
  // Modbus connection type, choice between "SERIAL/RTU" and "TCP/IP" 
  "tcp/ip": {
    // TCP/IP connection properties (ignored if connectionType is "SERIAL/RTU")
    "host": "192.168.x.x",
    // IP address of Modbus server (mandatory if connectionType is "TCP/IP") 
    "port": 502
    // Port of Modbus server (default is 502, if not stated)
  },
  "serial/rtu": {
    // Serial/RTU connection properties (ignored if connectionType is "TCP/IP") 
    "serialPort": "SERIAL_PORT",
    // Serial port location such as /dev/ttyS0 (mandatory if connection type is "SERIAL/RTU")
    "baudRate": 115200,
    // Baud rate for serial connection (default is 115200, if not stated)
    "dataBits": 8,
    // DataBits for Modbus RTU (default is 8, if not stated) 
    "stopBits": 1,
    // StopBits for Modbus RTU (default is 1, if not stated)
    "bitParity": "NONE"
    // BitParity for Modbus RTU, can be "NONE", "EVEN", "ODD" (default is "NONE", if not stated)
  },
  "responseTimeoutMs": 200,
  // Wait time for respond from slaves/servers (default is 200, if not stated)
  "registerReadPeriodMs": 500
  // Period of reading all registers/devices (default is 500, if not stated) 
}

devicesConfiguration.json

Devices configuration file contains information necessary to define templates, which include registers that bind to Wolkabout IoT Platform sensors/actuators/alarms/configurations, and then devices, with their information, and a template. This is the guide by which the module will register devices, and then send data for.

You define templates, that are described by their name, and mappings.

{
  "templates": [
    {
      "name": "<TEMPLATE_NAME>",
      "mappings": [
        // define mappings
      ]
    }
  ]
}

After doing so, you define all the mappings inside a template.

Single mapping includes one or more Modbus registers that produce a single sensor/actuator/alarm/configuration on a device, on the platform. They're characterized by a name, and a reference key, used to identify them, by their register type (necessary), data type (necessary), operation type (necessary for some output types), and mapping type.

There are optional features you can enable for mappings, such as:

  • Unit type
  • Deadband and Frequency filtering
  • Repeated write
  • Default value
  • Safe Mode value
  • AutoLocalUpdate toggle
  • AutoReadAfterWrite toggle

Register types:

  • Read & Write:
    • COIL
    • HOLDING_REGISTER
  • Read Only:
    • INPUT_REGISTER
    • INPUT_CONTACT

Data types:

- UINT16/INT16
- BOOL
- UINT32/INT32 (with merge operations)
- FLOAT (with merge operation)
- STRING (with stringify operations)

Operation types:

- `MERGE_BIG_ENDIAN` or `MERGE_LITTLE_ENDIAN` (in combination with `"dataType": "UINT32/INT32"`)
- `MERGE_FLOAT_BIG_ENDIAN` or `MERGE_FLOAT_LITTLE_ENDIAN` (in combination with `"dataType": "FLOAT"`)
- `TAKE_BIT` (in combination with `"dataType": "BOOL"`)
- `STRINGIFY_ASCII_BIG_ENDIAN`, `STRINGIFY_ASCII_LITTLE_ENDIAN`, `STRINGIFY_UNICODE_BIG_ENDIAN` or `STRINGIFY_UNICODE_LITTLE_ENDIAN` (in combination with `"dataType": "STRING"`)

Unit types:

You can add a field "unit": "CELSIUS" to a mapping that will register the feed on the platform with the GUID of the unit on the platform.

Deadband and frequency filters:

You can add fields "deadBandFilter":0.1 and "frequencyFilterValue":1 to add a deadband and frequency filter to your mappings.

Repeated write

If this feature is enabled, it will write overwrite the value in the register after some time even if the value has not changed. You can add a field "repeat":500 and the value will be overwritten every 500ms.

Default value

If you want to set a value that will be written in as soon as the application launches, write it in as the default value. You can add a field "defaultValue":123 and it will be written in when the application launches.

Safe Mode value

If you want a value written into a mapping when the gateway and the module lose connectivity with the platform, you can write the value here. You can add a field "safeMode":123 and it will be written in when the communication with the platform is lost.

AutoLocalUpdate

The software keeps a local copy of the value of all mappings, and the message to the platform about updates is sent when a new value gets read from the modbus register. By default actuation will write into the register, but not in the local copy, so the value will be read in the next read cycle and sent to the platform. If you want the local copy of mapping values to be updated, set the "autoLocalUpdate":true for the mapping. This will result in a message about the feed to never be sent to the platform, and the platform will just implicitly take it as the actuation was successful.

AutoReadAfterWrite

On actuation, the module will write the value directly into the registers as needed. But it will also read the same registers, and notify the platform of value changes if detected - updating the local copy of the values as well. If you want this behavior to be turned off, set the "autoReadAfterWrite":false for the mapping. This will disable the automatic read after writing into a mapping.

{
  // Inside of a template
  "name": "<TEMPLATE_NAME>",
  "mappings": [
    {
      "name": "mappingName",
      // Register name
      "reference": "mappingReference",
      // Unique reference used to differ register on WolkAbout IoT Platform
      "minimum": -32768,
      // Minimum value that can be held in register. Required for visualization on WolkAbout IoT Platform
      "maximum": 32767,
      // Maximum value that can be held in register. Required for visualization on WolkAbout IoT Platform
      "address": 0,
      // Register address
      "registerType": "INPUT_REGISTER",
      // Register type - "INPUT_REGISTER" or "HOLDING_REGISTER_ACTUATOR" or "HOLDING_REGISTER_SENSOR" or "INPUT_CONTACT" or "COIL"
      "dataType": "INT16"
      // Data type stored in register - "INT16" or "UINT16" or "REAL32" for "INPUT_REGISTER"/"HOLDING_REGISTER_ACTUATOR"/"HOLDING_REGISTER_SENSOR" register type, and "BOOL" for "COIL"/"INPUT_CONTACT"
    },
    {
      "name": "mappingName2",
      "reference": "mappingReference2",
      "minimum": -4000,
      "maximum": 4000,
      "address": 1,
      "registerType": "HOLDING_REGISTER",
      "dataType": "FLOAT",
      "deadbandValue": 4.0,
      // Optional - Indicates a change in value of the mapping that is insignificant data. Applicable to numeric mappings.
      "frequencyFilterValue": 250
      // Optional - When register changes value often, disregard changes until X milliseconds pass since last accepted value.
    },
    {
      "name": "mappingName3",
      "reference": "mappingReference3",
      "address": 1,
      "registerType": "INPUT_CONTACT",
      "dataType": "BOOL"
      // This is where for the first time, we override the default Mapping type
    },
    {
      "name": "mappingName4",
      "reference": "mappingReference4",
      "address": 2,
      "registerType": "HOLDING_REGISTER",
      "operationType": "MERGE_BIG_ENDIAN",
      "dataType": "UINT32"
    },
    {
      "name": "mappingReference5",
      "reference": "mappingReference5",
      "address": 3,
      "registerType": "COIL",
      "dataType": "BOOL",
      "writeOnly": true
      // If the mapping needs to be Write-Only, you put this attribute.
    },
    {
      "name": "mappingReference6",
      "reference": "mappingReference6",
      "address": 10,
      "addressCount": 10,
      "registerType": "HOLDING_REGISTER",
      "dataType": "STRING",
      "operationType": "STRINGIFY_ASCII_BIG_ENDIAN",
      "autoLocalUpdate": true,
      "autoReadAfterWrite": false
    },
    {
      "name": "mappingReference7",
      "reference": "mappingReference7",
      "address": 8,
      "bitIndex": 0,
      "registerType": "INPUT_REGISTER",
      "operationType": "TAKE_BIT",
      "dataType": "BOOL"
    }
  ]
}

After you completely defined a template, you can list a device.

{
  "devices": [
    {
      "name": "<DEVICE_NAME>",
      // User readable device name, necessary to register a device
      "key": "<DEVICE_KEY>",
      // Unique device key
      "slaveAddress": 1,
      // Slave address (obligatory if connection type is "SERIAL/RTU"), must be unique in this list 
      "template": "<TEMPLATE_NAME>"
      // Name of defined template 
    }
    // other devices
  ]
  // templates
}

In "TCP/IP" mode, device count is maxed at 1, and you don't need to state a slaveAddress for the device.

If the user happens to enter an invalid template name, the device won't be created. Module will function if at least one device is valid. If there are no devices that have been inputted correctly, the module will exit out, and used will be notified.