Simple read/write on Modbus

Prerequisites

  • Modbus connector

  • A Modbus device or a virtualized Modbus device like oitc/modbus-server:1.1.2

Checkout branches

git checkout origin/osp-modbus-configuration .

For the virtual device

docker container run -p 5020:5020 --name=modbus-test oitc/modbus-server:1.1.2

Description

Note

The complete example can be checkout with git checkout origin/example-modbus_simple_read_write_example.

In this tutorial you will learn how to :

  • Register a Modbus slave

  • Read/write Modbus values from the slave

This illustration demonstrates the establishment of a loop involving a Modbus coil (boolean) located at address 0. The Modbus module will continuously perform read and write operations with a 1-second interval. With each iteration, the value will be inverted.

Steps

1. Register a Modbus slave

The first step is to create a “slave” file in which we define how to connect to the slave. Replace the IP by the IP of the Modbus device. We are using a read frequency of 1 second for our test. This means the input from the Modbus will be updated every second.

Content : root/plc/slave-1/slave.modbus

{
    "host": "10.0.2.15:5020", 
    "moduleId": "modules.modbus.modbus-1",
    "readPeriod": {
        "value": 1,
        "unit": "SECONDS"
    }
}

2. Create a coil input

Content : root/input-coil/value.ospp

{
  "name": "input-coil",
  "description": "input-coil",
  "type": "BOOLEAN"
}

We introduce an entity called owner.modbus. This file serves the purpose of indicating our intention to interpret address 0 as a coil for reading. The slave ID field serves as the connection identifier to the corresponding slave device.

Content : root/input-coil/owner.modbus

{
  "address": 0,
  "endianness": "BIG",
  "modbusType": "COIL",
  "slaveId": "root.plc.slave-1",
  "valueType": "BOOLEAN"
}

1. Create an output file to write value on the modbus device

The output.modbus file describes how we want to write to the Modbus device, but does nothing if nobody interacts with it.

Content : root/output-coil/output.modbus

{
    "address": 0,
    "endianness": "BIG",
    "modbusType": "COIL",
    "slaveId": "root.plc.slave-1",
    "valueType": "BOOLEAN"
}  

4. Call the output from an event.

To create a state inversion, this test adds some simple logic to read the value from address 0 then call the output with the inverted value using the expression capabilities.

Content : root/input-coil/callback.ospp

{
    "linkedOutputs": [
        {
            "outputId": "root.output-coil",
            "transform": "not(value)"
        }
    ]
}

1. Push the configuration and check the result

git add .
git commit -m "Add simple modbus test"
git push

Check the value of the device :

docker container logs -f modbus-test

Logs must show :

The connexion from `osp-modbus` :
2023-08-14 14:10:48,922 MainThread       DEBUG    sync           :347      Started thread to serve client at ('172.17.0.1', 51850)
2023-08-14 14:10:48,922 Thread-1         DEBUG    sync           :46       Client Connected [172.17.0.1:51850]

A write request between 0 and 1 on the coil address (each second) :

2023-08-14 14:11:23,922 Thread-1         DEBUG    sync           :229      send: [ReadBitResponse(1)]- b'003d0000000401010100'
2023-08-14 14:11:23,922 Thread-1         DEBUG    sync           :199      Handling data: 0x0 0x3e 0x0 0x0 0x0 0x6 0x1 0x5 0x0 0x0 0xff 0x0
2023-08-14 14:11:23,923 Thread-1         DEBUG    socket_framer  :147      Processing: 0x0 0x3e 0x0 0x0 0x0 0x6 0x1 0x5 0x0 0x0 0xff 0x0
2023-08-14 14:11:23,923 Thread-1         DEBUG    factory        :137      Factory Request[WriteSingleCoilRequest: 5]
2023-08-14 14:11:23,923 Thread-1         DEBUG    context        :63       validate: fc-[5] address-1: count-1
2023-08-14 14:11:23,923 Thread-1         DEBUG    context        :90       setValues[5] 1:1
2023-08-14 14:11:23,923 Thread-1         DEBUG    context        :77       getValues fc-[5] address-1: count-1
2023-08-14 14:11:23,923 Thread-1         DEBUG    sync           :229      send: [WriteCoilResponse(0) => 1]- b'003e0000000601050000ff00'
2023-08-14 14:11:24,921 Thread-1         DEBUG    sync           :199      Handling data: 0x0 0x3f 0x0 0x0 0x0 0x6 0x1 0x1 0x0 0x0 0x0 0x1
2023-08-14 14:11:24,922 Thread-1         DEBUG    socket_framer  :147      Processing: 0x0 0x3f 0x0 0x0 0x0 0x6 0x1 0x1 0x0 0x0 0x0 0x1
2023-08-14 14:11:24,922 Thread-1         DEBUG    factory        :137      Factory Request[ReadCoilsRequest: 1]
2023-08-14 14:11:24,922 Thread-1         DEBUG    context        :63       validate: fc-[1] address-1: count-1
2023-08-14 14:11:24,922 Thread-1         DEBUG    context        :77       getValues fc-[1] address-1: count-1
2023-08-14 14:11:24,922 Thread-1         DEBUG    sync           :229      send: [ReadBitResponse(1)]- b'003f0000000401010101'
2023-08-14 14:11:24,923 Thread-1         DEBUG    sync           :199      Handling data: 0x0 0x40 0x0 0x0 0x0 0x6 0x1 0x5 0x0 0x0 0x0 0x0
2023-08-14 14:11:24,923 Thread-1         DEBUG    socket_framer  :147      Processing: 0x0 0x40 0x0 0x0 0x0 0x6 0x1 0x5 0x0 0x0 0x0 0x0
2023-08-14 14:11:24,923 Thread-1         DEBUG    factory        :137      Factory Request[WriteSingleCoilRequest: 5]
2023-08-14 14:11:24,923 Thread-1         DEBUG    context        :63       validate: fc-[5] address-1: count-1
2023-08-14 14:11:24,923 Thread-1         DEBUG    context        :90       setValues[5] 1:1
2023-08-14 14:11:24,923 Thread-1         DEBUG    context        :77       getValues fc-[5] address-1: count-1
2023-08-14 14:11:24,924 Thread-1         DEBUG    sync           :229      send: [WriteCoilResponse(0) => 0]- b'004000000006010500000000'