In the context of the Web of Things (WoT), a Binding Template is a blueprint that gives guidance on how to implement a specific IoT protocol, data format or IoT platform. The Core Binding Templates specification explains the overall mechanism and requirements for any binding to follow. This document gives implementation guidelines regarding the Modbus protocol, which is a well-known cost-effective IoT protocol for communication between industrial control and automation devices.

More specifically, this document defines a set of vocabulary terms that can be used inside a Thing Description document, and associated rules which allow to describe WoT operations using the Modbus protocol over the network. Additionally, relevant examples are provided to showcase different vocabulary terms and the associated behavior.

Introduction

The Modbus is a data communication protocol originally developed by Modicon which is now a part of Schneider Electric. The protocol was specifically designed for the remote management of the hardware devices in the industrial environment. For this reason, it has low level of abstraction and it has built in bit handling capabilities oriented to the direct control of the relays and generic contact statuses. The physical layer it is mainly an RS485 differential bus which has less susceptibility to the EMC interference. This Limit the usability of the protocol in short distance networks, typically within a kilometer. Due to its age the protocol does not implement any safety system, so usually when wide internet access is needed it is encapsulated in a TCP/IP protocol and with Ethernet as a physical layer. Thanks to this encapsulation strategy, the Modbus protocol can reach remote nodes deployed in distant facilities over the internet. Moreover, in the years due to its simplicity and cost-effectiveness the Modbus protocol becomes a standard all over the world. This fame together with the advancement of the microcontroller/microprocessor led to a shift on how applications pack the information. Today it is usual to store and read any type of data in the Holdings Registers like bit, bytes, words, float etc.

This document describes how the Web of Things specification can be use to present devices that use the Modbus protocol in a Thing Description. In particular, the document explain how to create valid URLs and Forms for the different operations that the Modbus protocol can perform. Developers are encouraged to use this document as an implementation guidelines and as a reference for the creation of their own binding implementations. The following sections will cover the URL format, the Vocabulary and a list of Form examples.

This document is a work in progress

URL format

Historically different URL scheme has been used inside the Modbus community: modbus+tpc://, modbus+ascii:// and modbus+rtu://. Considering that in Web of Things context all protocol binding templates are required to at least support the Internet Protocol, for the Modbus protocol modbus+tcp:// was selected as the only valid URL scheme. The following shows the typical structure of an URL of the Modbus protocol:
            modbus+tcp://{address}:{port}/{?unitID}
        

Where:

Modbus Vocabulary

This section describes the vocabulary used in the Modbus protocol. A protocol binding template implementation should use the vocabulary defined in this section to describe the different configuration that can be used to exchanged data between Web of Things.

Form terms

Vocabulary term Description Assignment Type
modbus:address Specifies the starting address of the Modbus operations required integer
modbus:unitID The Unit ID is usually not needed for ModbusTCP, since the IP-address works as unique identifier, but for compability reasons still often included required integer
modbus:quantity Specifies the amount of either registers or coils to be read or written to optional integer
modbus:pollingTime Modbus TCP maximum polling rate. The Modbus specification does not define a maximum or minimum allowed polling rate, however specific implementations might introduce such limits. Defined as integer of milliseconds. optional integer
modbus:timeout Modbus response maximum waiting time. Defines how much time the runtime should wait until it receives a reply from the device. optional integer
modbus:zeroBasedAddressing Modbus implementations can differ in the way addressing works, as the first coil/register can be either referred to as True or False. optional boolean
modbus:entity A registry type to let the runtime automatically detect the right function code optional Entity
modbus:function Function Code sent by the master in every request. Specifying the desired interaction. optional Function

Entity

A more user-friendly property to specify [[[#function]]]. The client will then determine the right function code to be applied in the modbus request. Futhermore, it can be used in multi-operation forms whereas modbus:function cannot (See the [[[#example-read-coil-entity]]])
Value Description
Coil Represent a modbus coil register. These entities can be read or written
DiscreteInput Represent a modbus discrete input. These entities can only be read
HoldingRegister Represent a modbus holding register. These entities can be read or written
InputRegister Represent a modbus input register. These entities can only be read
Notice that when used in conjunction with modbus:function, the value of modbus:function property should be ignored.

Function

Function Code class represent the value of the function field in a Modbus frame. The following table list the supported codes and their description:
Value Label Code Description
readCoil Read Single Coil 1 Read a single coil (i.e. boolean/bit access) value. Usually in the address range 00001-09999
readDeviceIdentification Read Device Identification 43 Read Device Identification for diagnostic purposes. Avaiable in the majority of Modbus implementations.
readDiscreteInput Read Discrete Inputs 2 Read Physical Discrete Inputs (bit access). Address range 10001-19999
readHoldingRegisters Read Multiple Holding Registers 3 Read Multiple Holding Registers (16 bit access). Address range 40001-49999
readInputRegisters Read Multiple Input Registers 4 Read Multiple Physical Input Registers (16 bits). Address range 30001-39999
writeMultipleCoils Write Multiple Coils 15 Write Multiple Physical Coils (internal bits). Address range 00001-09999
writeMultipleHoldingRegisters Write Multiple Holding Registers 16 Write Multiple Holding Registers (output registers, 16 bit). Address range 40001-49999
writeSingleCoil Write Single Coil 5 Write Single Physical Coil (internal bit). Address range 00001-09999
writeSingleHoldingRegister Write Single Holding Register 6 Write Single HoldingRegister (internal bits). Address range 40001-49999

Mappings

This section describes strategies and default values to employ protocol specific concepts within the WoT Interaction model.

Default mappings

The following table lists the default mappings between the protocol specific concepts and the WoT concepts. Please note that operations that are not listed in the table are not supported by this binding template. For example, since the Modbus protocol is a request-response based protocol, the subscribeevent operation is not supported.

Operation Default Binding
writeproperty "modbus:function": "writeSingleCoil"
invokeaction "modbus:function": "writeSingleCoil"
readallproperties "modbus:function": "readHoldingRegisters"
readmultipleproperties "modbus:function": "readHoldingRegisters"
writeallproperties "modbus:function": "writeMultipleHoldingRegisters"
writemultipleproperties "modbus:function": "writeMultipleHoldingRegisters"
The next table defines default values for other terms, regardless of their use in combination with one of the operations listed in the table above.
Operation Default Comments
modbus:quantity 1
modbus:zeroBaseAddressing false
modbus:timeout infinite

Possible mappings

Additional to the default mappings, users may decide to use other [[[#function]]]s for a specific operation. The following table lists examples of possible mappings for some operation types.

Operation Possible Binding
writeproperty "modbus:function":"writeMultipleCoils"
writeproperty "modbus:function":"writeSingleHoldingRegister"
readproperty "modbus:function":"readDeviceIdentification"
readproperty "modbus:function":"readDiscreteInput"
observeproperty "modbus:function":"readCoil"; "modbus:pollingTime": 1000

Examples

This section will present a set of examples about how the terms defined in this document can be used to describe and configure a Form. The [[[#example-read-coil]]] shows the minimal set of terms to configure a single coil reading using Modbus. Notice that the unitID is contained in the href as the first element of the path.
            {
                "href": "modbus+tcp://127.0.0.1:60000/1",
                "op": [
                    "readproperty"
                ],
                "modbus:function": "readCoil",
                "modbus:address": 1
            }
        
To describe forms with multiple operations types, the [[[#entity]]] keyword can be used to create a short description of the modbus endpoint.
                    {
                        "href": "modbus+tcp://127.0.0.1:60000/1",
                        "op": [
                            "readproperty",
                            "writeproperty"
                        ],
                        "modbus:entity": "Coil",
                        "modbus:address": 1
                    }
        
A TD processor will intepred [[[#example-read-coil-entity]]] configuration as the following:
                            {
                                "href": "modbus+tcp://127.0.0.1:60000/1",
                                "op": [
                                    "readproperty",
                                ],
                                "modbus:function": "readCoil",
                                "modbus:address": 1
                            },
                            {
                                "href": "modbus+tcp://127.0.0.1:60000/1",
                                "op": [
                                    "writeproperty",
                                ],
                                "modbus:function": "writeCoil",
                                "modbus:address": 1
                            },
        
Reducing effectively the verbosity of a TD. Thanks to the expressiveness of the Modbus ontology users can describe also the total number of registries read or wrote in a WoT operation. [[[#example-read-holding]]] shows how to read or write 8 HoldingRegisters.
                            {
                                "href": "modbus+tcp://127.0.0.1:60000/1",
                                "op": [
                                    "readproperty",
                                    "writeproperty"
                                ],
                                "modbus:entity": "HoldingRegister",
                                "modbus:address": 40001,
                                "modbus:quantity": 8
                            }
                
When possible WoT consumers will use Modbus features to read the desired amount of data with a single protocol request. However, it may be possible to still specify a total length for Modbus operations that do not support reading or writing on a range of registers (see [[[#example-read-coil-range]]]). In these circumstances consumers will perform different requests to satisfy the configuration requirements.
                            {
                                "href": "modbus+tcp://127.0.0.1:60000/1",
                                "op": [
                                    "readproperty",
                                    "writeproperty"
                                ],
                                "modbus:entity": "Coil",
                                "modbus:address": 1,
                                "modbus:quantity": 8
                            }
                
Another notable configuration of a form using the Modbus vocabulary is the polling mechanism. Thanks to the keyword pollingTime the user can indicate the intervals for observing a particular set of registers. Supposing that the device knows that the value of coil register 1 does change every 1000 ms, in [[[#example-polling]]], it suggest that the polling time should not be faster than 10 ms. WoT consumers may still create requests faster than the specified time but it should be taken as a reasonable default for observing a property.
            {
                "href": "modbus+tcp://127.0.0.1:60000/1",
                "op": [
                    "observeproperty"
                ],
                "modbus:entity": "Coil",
                "modbus:pollingTime": 1000,
                "modbus:address": 1
            }
        
Finally, [[[#full-td]]] shows a complete device described using Modbus ontology.
                {
                  "@context": [
                      "https://www.w3.org/2019/wot/td/v1",
                      {
                          "modbus": "https://www.example.com/ns/modbustcp"
                      }
                  ],
                  "title": "ModbusPLC",
                  "description": "An industrial machine, retrofitted to be IoT capable.",
                  "id": "uri:dev:ModbusTCPThing",
                  "securityDefinitions": {
                      "nosec_sc": {
                          "scheme": "nosec"
                      }
                  },
                  "security": "nosec_sc",
              
                  "base": "",
                  "properties": {
                      "limitSwitch1": {
                          "title": "downLimitSwitch",
                          "type": "boolean",
                          "description": "Limit switch moving downwards",
                          "forms": [
                              {
                                  "op": "readproperty",
                                  "href": "modbus+tcp://192.168.178.32:502",
                                  "modbus:function": "readDiscreteInput",
                                  "modbus:quantity": 1,
                                  "modbus:address": 10003,
                                  "modbus:unitID": 1,
                                  "contentType": "application/octet-stream"
                              }
                          ]
                      },
                      "limitSwitch2": {
                          "title": "forwardLimitSwitch",
                          "type": "boolean",
                          "description": "Limit Switch moving forward",
                          "forms": [
                              {
                                  "op": "readproperty",
                                  "href": "modbus+tcp://192.168.178.32:502",
                                  "modbus:function": "readDiscreteInput",
                                  "modbus:quantity": 1,
                                  "modbus:address": 10002,
                                  "modbus:unitID": 1,
                                  "contentType": "application/octet-stream"
                              }
                          ]
                      },
                      "moveDown": {
                          "title": "moveDown",
                          "type": "boolean",
                          "description": "Down Motor Status (single coil). PLC output, can be written to control the motor.",
                          "forms": [
                              {
                                  "op": [
                                      "writeproperty",
                                      "observeproperty",
                                      "readproperty"
                                  ],
                                  "href": "modbus+tcp://192.168.178.32:502",
                                  "modbus:entity": "Coil",
                                  "modbus:quantity": 1,
                                  "modbus:address": 6,
                                  "modbus:unitID": 1,
                                  "modbus:pollingTime": 100,
                                  "contentType": "application/octet-stream"
                              }
                          ]
                      },
                      "moveForward": {
                          "title": "moveForward",
                          "type": "boolean",
                          "description": "Forward Motor Status (single coil). PLC output, can be written to control the motor.",
                          "forms": [
                              {
                                   "op": [
                                      "writeproperty",
                                      "observeproperty",
                                      "readproperty"
                                  ],
                                  "href": "modbus+tcp://192.168.178.32:502",
                                  "modbus:entity": "Coil",
                                  "modbus:quantity": 1,
                                  "modbus:address": 3,
                                  "modbus:unitID": 1,
                                  "modbus:pollingRate": 100,
                                  "contentType": "application/octet-stream"
                              }
                          ]
                      }
                  }
              }