Modbus to DNP3 conversion example

Simulate a slave using Modbuspal

The first step involves simulating a Modbus TCP slave using the modbuspal simulator. Note: the simulator requires a Java Virtual Machine.

Download and start modbuspal

  • Download the ModbusPAL Modbus slave simulator and start it.
1162
  • Change TCP Port to 5020 and make sure that such port is open in the firewall configuration of the machine running ModbusPAL.

Add a new unit

  • Press Add under Modbus slaves, select 1 and press Add.
  • A new entry should appear reporting unknown slave as name

Add some holding registers and coil to the simulated unit

  • Click on the eye icon next to unknown slave, the following dialog should appear.
846
  • Select the Holding registers tab, click on Add, and then on Add again in the dialog.
  • Select the Coil tab, click on Add, and then on Add again in the dialog.
  • Return to the main dialog and click Run

Change some holding register and coil values

  • Holding register and coil values can be changed by clicking on the value cell of a table entry and inserting the desired value.

Configure ESF modbus driver to read some data from the simualtor

Install the Modbus Driver

  • Access ESF Web UI by opening the http://<gateway ip> URL with a browser, default username/password are admin/admin.
  • Navigate to the System -> Packages section.
3370 3326

Create a Modbus Driver instance

  • Navigate to the Drivers and Assets section of ESF Web UI.
  • Click on the New Driver button.
  • Select org.eclipse.kura.driver.modbus as Driver Factory.
  • Enter an arbitrary name for the driver instance in the Driver name field, for example modbusSimulator.
  • Click on the modbusSimulator entry in the table.
3376
  • Set modbus.tcp-udp.ip to the IP address/hostname of the machine running ModbusPAL.
  • Set modbus.tcp-udp.port to 5020.

Configure the driver to read some data

  • Click on the modbusSimulator entry in the table.
  • Click on the New Asset button.
  • Enter an arbitrary name for the asset instance in the Asset Name field, for example modbusAsset.
  • Configure the asset as shown in the picture below, this will create an Asset configuration that reads holding registers 1 and 2 and coils 1 and 2.
3374
  • Click on the Data tab, if the Modbus connection is ok, the current register values should be shown.

Read the values periodically using a Wire Graph

In order to create some logic that periodically reads the values, a simple Wire Graph can be created as follows:

  • Navigate to the Wire Graph section of ESF Web UI.
  • Drag and Drop a Timer component in the canvas
  • Assign an arbitrary name to the timer component, for example modbusReadTimer, and press OK.
  • Click on the component
  • Enter 1 as simple.interval, this property defines the poll rate.
3372
  • Drag a WireAsset component to the canvas.
  • Select modbusAsset from the list and click OK. This will add the asset defined above to the graph.
  • Drag a Logger component to the canvas and assign a name to it, for example logger.
  • Click on the logger component.
  • Set log.verbosity to VERBOSE. The logger component will print the data produced by the asset in device log.
  • Connect the components as shown in the picture and press Apply.
3378

The logger is useful for inspecting the values emitted by another Wire Component.
The logs can be viewed by opening an SSH connection to the device, for example using the following command in Linux/OSX terminal or Windows 10 PowerShell:

ssh root@<device ip>

The default password is eurotech.

Once the SSH connection is established, ESF logs can be viewed using the following command:

tail -F /var/log/kura.log

Messages like the following should be printed every second:

2020-04-21T11:27:17,346 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger - Received WireEnvelope from org.eclipse.kura.wire.WireAsset-1587466852771-26
2020-04-21T11:27:17,348 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger - Record List content:
2020-04-21T11:27:17,350 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -   Record content:
2020-04-21T11:27:17,352 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -     holdingReg2_timestamp : 1587468437346
2020-04-21T11:27:17,354 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -     coil2_timestamp : 1587468437343
2020-04-21T11:27:17,356 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -     holdingReg1_timestamp : 1587468437345
2020-04-21T11:27:17,358 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -     coil2 : false
2020-04-21T11:27:17,360 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -     coil1 : false
2020-04-21T11:27:17,362 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -     assetName : modbusAsset
2020-04-21T11:27:17,364 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -     holdingReg1 : 0
2020-04-21T11:27:17,366 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -     holdingReg2 : 0
2020-04-21T11:27:17,367 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -     coil1_timestamp : 1587468437343
2020-04-21T11:27:17,370 [DefaultQuartzScheduler_Worker-9] INFO  o.e.k.i.w.l.Logger -

Install DNP3 slave driver

  • Download the com.eurotech.framework.dnp3.slave.driver.armv7hf.feature deployment package.
  • Access ESF Web UI by opening the http://<gateway ip> URL with a browser, default username/password are admin/admin.
  • Navigate to the System -> Packages section.
  • Click on the Install/Upgrade button.
  • Select the com.eurotech.framework.dnp3.slave.driver.armv7hf.feature deployment package downloaded before and click on Submit

Create a DNP3 slave driver instance

  • Navigate to the Drivers and Assets section of ESF Web UI.
  • Click on the New Driver button.
  • Select com.eurotech.framework.internal.dnp3.slave.driver.Dnp3SlaveDriver as Driver Factory.
  • Enter an arbitrary name for the driver instance in the Driver name field, for example dnp3Slave.

With the default configuration, the slave will listen on port 20000 and use 1024 as link layer address.

Open port 20000 in device firewall

  • Navigate to the Firewall section of ESF Web UI.
  • Click on the New button.
  • Enter 20000 as Port or Port Range and click Submit
3378
  • Click Apply

Define a mapping for the Modbus values

A simple mapping between the data that the Modbus driver is currently configured to read and DNP3 data points can be the following:

  • Modbus Holding Register 1 <-> DNP3 Analog Input 0
  • Modbus Holding Register 2 <-> DNP3 Analog Input 1
  • Modbus Coil 1 <-> DNP3 Binary Input 0
  • Modbus Coil 2 <-> DNP3 Binary Input 1

The mapping can be defined by creating a DNP3 slave driver Asset with suitable channels, and configuring the Wire Graph to write the data coming from Modbus asset into DNP3 asset.

Define a DNP3 asset for input values

  • Navigate to the Drivers and Assets section of ESF Web UI.
  • Click on the dnp3Slave entry in the table.
  • Click on the New Asset button.
  • Enter an arbitrary name for the asset instance in the Asset Name field, for example dnp3Input.
  • Configure the asset as shown in the picture below.
2754

The following rules are important for the mapping:

  • All DNP3 slave driver asset channels must use READ_WRITE as type.
  • The values of the name and value.type properties of DNP3 slave driver channels must exactly match the corresponding values of Modbus asset channels.

Configure the Wire Graph to perform the mapping

At this point the DNP3 Asset is configured but no component is updating its values yet.

  • Navigate to the Wire Graph section of ESF Web UI.
  • Drag and drop a WireAsset component to the canvas.
  • Select dnp3Input from the list and click OK.
  • Connect modbusAsset to dnp3Input
3364

At this point, every time the timer ticks, the values are read from the Modbus asset and written into the DNP3 asset. If should be possible to retrieve the values by connecting a DNP3 master to the device. With default DNP3 driver configuration, changes in modbus values should be reported with unsolicited messages as well.

The following details about Wires, Assets and their interaction are important to understand how the mapping works:

Wires
  • A message flowing in the graph is called WireEnvelope.
  • Each WireEnvelope contains a list of WireRecords.
  • Each WireRecord contains a set of key-value pairs, called properties.
    • The property key is a string
    • The property value can have one of the following types
      • BOOLEAN
      • BYTE_ARRAY
      • DOUBLE
      • INTEGER
      • LONG
      • FLOAT
      • STRING
WireAsset
  1. Every time a WireAsset receives an envelope on its input port, it will read the values of all of its channels with READ or READ_WRITE type.
  • The result is emitted as a WireEnvelope with a single WireRecord
  • The WireRecord contains the following properties:
    • a property with key assetName, value type STRING and the emitting asset asset name as value
      For each channel in asset configuration with READ or READ_WRITE type named <channel name>:
    • a property with key = <channel name>, value type = value.type in channel configuration and value = value obtained from read operation. This property will be present only if the read operation is successful.
    • a property with key = <channel name>_timestamp value type = LONG reporting a timestamp in milliseconds since UNIX epoch. This property will be present only if the timestamp.mode Asset configuration property is PER_CHANNEL
    • a property with key = <channel name>_error value type = STRING reporting an error message. This property will be present only if read operation fails and the emit.errors Asset configuration property is set to true.

The structure above can be seen in logger output. Read operation is performed regardless of input record content.

Additionally, the Wire Graph can also be used to update asset values through write operations, according to the following rule.

  1. Every time a WireAsset receives an envelope on its input port, for each property contained in the received WireRecords with key <key>, value type <value type> and value <value>:
  • If a channel with name <key> is defined in asset configuration whose value.type is equal to <value type>, then the Asset will write <value> to the channel.

In the setup constructed so far, the Timer emits an envelope every second, triggering a read from the Modbus asset due to rule 1. The envelope emitted by the Modbus Asset will trigger a write on the DNP3 slave Asset updating the values, due to rule 2.

It is also possible to use other wire components to update the values in DNP3 Asset (for example the ScriptFilter), as long as rule 2. is respected.

### Updating modbus register values

It is also possible to use DNP3 commands to update Modbus register values.

In order to do this, another pair of assets must be created.

Create a DNP3 output asset

  • Navigate to the Drivers and Assets section of ESF Web UI.
  • Click on the dnp3Slave entry in the table.
  • Click on the New Asset button.
  • Enter an arbitrary name for the asset instance in the Asset Name field, for example dnp3Output.
  • Configure the asset as shown in the picture below.
3358

Sending CROB or analog output commands to the gateway will have the effect of changing the value of corresponding data points. The received values can be retrieved using the Wire Graph.

In case of CROB commands, the control codes that set binary output status value to the on and off state can be defined in global driver configuration.

Create a test graph for the output asset

  • Navigate to the Wire Graph section of ESF Web UI.
  • Drag and Drop a Timer component in the canvas
  • Assign an arbitrary name to the timer component, for example modbusWriteTimer, and press OK.
  • Click on the component
  • Enter 1 as simple.interval.
  • Drag a WireAsset component to the canvas.
  • Select dnp3Output from the list and click OK. This will add the asset defined above to the graph.
  • Connect the components as shown in the picture and press Apply.
3352

Sending commands from a DNP3 master for index 0 and 1 should cause the received values to be printed on the log every second.

#### Create a Modbus asset for writing output registers

  • Click on the modbusSimulator entry in the table.
  • Click on the New Asset button.
  • Enter an arbitrary name for the asset instance in the Asset Name field, for example modbusWrite.
  • Configure the asset as shown in the picture below, this will create an Asset configuration that writes to holding registers 10 and 11 and coils 10 and 11.
3360

The modbus asset above will implement the following mapping

  • DNP3 Analog Output Status 0 <-> Modbus Holding Register 10
  • DNP3 Analog Output Status 1 <-> Modbus Holding Register 11
  • DNP3 Binary Output Status 0 <-> Modbus Coil 10
  • DNP3 Binary Output Status 1 <-> Modbus Coil 11

#### Add the Modbus write asset to the Wire Graph

  • Navigate to the Wire Graph section of ESF Web UI.
  • Drag and drop a WireAsset component to the canvas.
  • Select modbusWrite from the list and click OK.
  • Connect dnp3Output to modbusWrite
3368

The values sent by the DNP3 master will now be written to the modbus slave every second.

The output mapping works due to the same rules that apply for the read branch.

Using listen mode

DNP3 Assets support emitting WireEnvelopes spontaneously when a command is received, this allows to remove the modbusWriteTimer component and perform write operations to Modbus only on event.

  • Navigate to the Wire Graph section of ESF Web UI.
  • Delete the modbusWriteTimer component.
  • Select the dnp3Output component.
  • Tick the listen flag on all channels
  • Click Apply
3368