Products:

Unserver User Manual

Overview

Unserver is an application that provides a simple interface for communicating with device networks using Modbus protocol.

Unserver maintains a connection to devices and exposes their data as a simple unified HTTP API.

Other applications can easily integrate with the provided API and perform read/write operations across the network:

        +-------------+
        | HMI Clients |
        +------+------+   ^
               |          | tags
         +-----+------+
         |  UNSERVER  |
         +-----+------+   ^
               |          | Modbus data
       +-------+--------+
       |                |
+------+-----+   +------+-----+
| Modbus PLC |   | Modbus PLC |
+------------+   +------------+

To make access to data simple and independent of the underlying protocol, Unserver uses a concept of tags.

A tag is an object that consists of one or more properties. Each property corresponds to a specific Modbus address or a range of addresses.

Modbus
Addresses                   
                            Tag
+----+  +--------+
|C1:1|->|        |  +-----------------+
+----+  |        |->|IsPumpOn:   True |
        |UNSERVER|  |EmergencySw:False|
+----+  |        |  +-----------------+
|C2:0|->|        |
+----+  +--------+

You can find out more about tags in the tags section.

Supported Protocols

This version of Unserver can communicate with Modbus RTU and Modbus TCP devices.

Main Features

  • Combining multiple Modbus addresses into tags
  • Easy and transparent JSON-based configuration
  • HTTP/REST API for integrating with high-level systems
  • Tag polling
  • Automatic data type conversion
  • Tag caching

System Requirements

  • Windows XP SP3 or later
  • .NET Framework 4.0 or later
  • 512MB RAM
  • 10 Mb of disk space

Installation

Before you proceed with the installation:

  1. Please make sure you PC meets the system requirements.

  2. Download the latest Unserver distributable.

Extract Unserver Files

Simply extract the unserver-*.zip archive you have downloaded into a new directory. We will call this directory a “working directory”.

URL Reservation

This section only applies to Windows 7 and newer. Windows XP doesn’t require URL reservation.

The latest versions of Windows require all programs that use HTTP to make a “URL Reservation”, which means the program will be allowed to listen to a specific URL on the machine.

Unserver can add the URL reservation automatically when it first runs, but Windows will display a dialog where you will need to allow this.

Alternatively, you can do this manually by running the following command as an administrator. Make sure to replace DOMAIN/User with your actual username.

netsh http add urlacl url=http://+:9000/ user="DOMAIN/User"

To have Unserver add the URL reservation automatically:

  1. Open the working directory and launch Unserver by running unserver.exe
  2. If a dialog appears, click “Yes”
  3. Make sure there are no error messages in the console
  4. Close Unserver

There may be an error message regarding the inability to open a serial port. This is OK at this point.

Once the URL reservation is added, the dialog won’t appear again, unless you configure a different TCP port to be used by Unserver.

Configure Windows Firewall

This step is required if you intend to access Unserver’s HTTP API from other computers in a local network.

To allow connections to Unserver, you also need to add a new inbound rule for the port Unserver is using (9000 by default).

Run the following command as administrator. Change the port value if you are using a non-default one:

netsh advfirewall firewall add rule name="Unserver Port" dir=in action=allow protocol=TCP localport=9000

Alternatively, you can use Windows Firewall GUI to add an inbound port rule.

Installing the Background Service (Optional)

If you want Unserver to start automatically and run continuosly in the background, you can install it as a Windows Service.

  1. Open Windows command propt cmd.exe as adminstrator
  2. Navigate to Unserver working directory
  3. Run unserver.exe install

To start the service after installation:

unserver.exe start

You can find out more about managing the Unserver background service, in the command line reference.

Activation

In the absense of a valid activation key, Userver will run trial mode.

Trial mode has all the features, but the program will shut down after a certain period of time.

To run in production mode:

  1. Purchase an activation key
  2. Save the key file to Unserver working directory
  3. Run as usual

Getting Started

This section will demonstrate the most basic configuration of Unserver to communicate with a single Modbus device.

To learn about more advanced configuration options, review configuration, tags, API sections.

Prerequisites

  • Unserver should be installed on your PC
  • A Modbus RTU or TCP device/network, physically connected to the PC

Step 1: Configure Unserver

Open network.json - a text file located in Unserver working directory. The purpose of network.json is to setup communication links used by Unserver to read and write data.

Adjust the configuration file to match your setup. For example, use the following code if you have a Modbus TCP device:

{
  // list of communication links used by Unserver
  "links":[
    {
      "name": "demo-link",
      "protocol": "modbus-tcp",
      "settings": {
        "ip": "127.0.0.1",
        "port": 502
      }
    }
  ],
  // list of devices connected to communication links
  "devices": [
    {
      "name": "demo-device",
      "link": "demo-link"
      "address": "1"
    }
  ],
  // API endpoint configuration
  "api": {
    "port": 9000
  }
}

Adjust the ip property of the link to match the IP address of your Modbus device.

Adjust the address property to match Slave ID of the device.

To connect to a Modbus RTU device, use a different configuration similar to this:

{
  // list of communication links used by Unserver
  "links":[
    {
      "name": "demo-link",
      "protocol": "modbus-rtu",
      "settings": {
        "port": "COM1",
        "baudRate": 9600,    
        "dataBits": 8,
        "parity": "none",
        "stopBits": 1,
        "timeoutMs": 500
      }
    }
  ],
  // list of devices connected to communication links
  "devices": [
    {
      "name": "demo-device",
      "link": "demo-link"
      "address": "1"
    }
  ],
  // API endpoint configuration
  "api": {
    "port": 9000
  }
}

Adjust the serial port setting to match your setup.

In both cases above, we have initialized a Modbus communication link named demo-link and added one device named demo-device.

The next step is defining some tags.

Step 2: Create Tags

The purpose of the ‘tags.json’ file is to configure ‘tags’, which map individual Modbus addresses onto Unserver API endpoints.

Open tags.json, and put the following code in it:

[
  {
    "name": "demo-tag", "device": "demo-device", 
    "properties": [
      // you can change 'HR0' to another Modbus address
      { "name": "demo-property", "address": "HR0" }
    ]
  }
]

With this configuration we have created one tag: demo-tag with a single property: test-property.

The value of the property will be read/written using Holding Register 0 of the device and interpreted as a 16-bit integer.

Step 3: Test the Tag

Run unserver.exe and make sure there are no error messages in the console. If there are errors, see troubleshooting for possible solutions, as the issue is probably a simple misconfiguration.

In a web browser navigate to http://localhost:9000/tags/demo-tag

You should now see the current value of the tag:

{
    "success":true,
    "name":"demo-tag",
    "data":{
        "demo-property": 0
    }
}

Or you will see an unsuccessful response:

{
    "success":false,
    "name":"demo-tag",
    "message":"Timeout while communicating with a field device"
}

This means Unserver does not get a response from the device.

Again, if something doesn’t work, please refer to the troubleshooting page for solutions.

Modbus Network Settings

This section covers Unserver communication settings in detail. They are normally stored in the network.json file.

Editing JSON Configuration Files

All configuration files used by Unserver are in JSON format. JSON is a simple text-based format that allows to describe structured data.

You can use any text editor to edit .json files. But it’s easier to use a more powerful free editor with syntax highlighting and validation, for example Visual Studio Code.

JSON Schema Validation

Every Unserver configuration files comes with a corresponding .schema.json file, which allows text editors to check the file for errors during editing.

Some editors don’t support JSON schema by default or require additional setup.

network.json File Reference

Type: object array

The links property is an array of objects, each representing a communication link, for example:

"links":[
  { 
    /* link configuration object */ 
  },
  { 
    /* another link configuration object */ 
  }
]

For Modbus RTU communication links, a configuration object looks like this:

{
  "name": "link-name",
  "protocol": "modbus-rtu",
  "settings": {
    "port": "COM1",
    "baudRate": 9600,    
    "dataBits": 8,
    "parity": "none",
    "stopBits": 1,
    "timeoutMs": 500
  }
}

For Modbus TCP communication links, a configuration object looks like this:

{
  "name": "link-name",
  "protocol": "modbus-tcp",
  "settings": {
    "ip": "127.0.0.1",
    "port": 502
  }
}

devices

Describes a list of devices connected to Unserver.

Type: object array

devices.name

Name is the identifier of a device. It is used to refer to a particular device when creating tags:

+-------------+  +--------------+
|    DEVICE   |  |     TAG      |
+-------------+  +--------------+
|name:  "plc1"|  |device: "plc1"|
+---------+---+  +----------+---+
          ^                 |
          |                 |
          +-----------------+

It’s best to pick something short, but descriptive for a device name.

The device name must be unique across all devices.

Type: string

Supported values: any

The name of a communication link used to connect to the device.

Type: string

devices.address

The address of the device on the Modbus network. Also knows as slave Id.

Type: string

Supported values: integer in range [1, 255]

devices.unsupportedOperations

A list of Modbus function codes not supported by the device.

This optional property instructs Unserver to avoid certain Modbus function codes when communicating with the particular device.

For instance, you could forbid code 15 (force multiple coils):

{
    ...
    "unsupportedOperations":[15]
}

This means Unserver will use the closest alternative - code 5 (force single coil) to write coils.

If the Modbus device supports all basic Modbus codes listed below, you don’t need to specify this property.

Type: integer array

Supported Values: any array of 1, 2, 3, 4, 5, 6, 15, 16 or empty array

api

Contains configuration of the HTTP API endpoint.

Type: object

api.port

TCP port used by Unserver HTTP API.

The port number is specified in HTTP requests of API clients:

http://localhost:9000/tags/example

This URL uses port 9000.

Type: integer

Supported values: number in range [1, 65535] After modifying this parameter Unserver will likely require adding a new URL reservation.

Tags

A tag is an object that consists of one or more properties. Each property corresponds to a specific Modbus address or a range of addresses.

Each tag has it’s own API endpoint through which API clients can read and write it’s value.

You can define tags in a file called tags.json.

tags.json contains an array of tag description objects. Each such object defines one tag.

Here’s an example of tags.json with a description for one tag:

[
  {
    "name": "tag1", "device": "device1", 
    "properties": [
      { "name": "p1", "address": "HR0" }
    ]
  }
]

This configuration would create a tag named tag1 with the following URL:

http://localhost:9000/tags/tag1

And the API response would look as follows:

{
    "name":"tag1",
    "data":{
        "p1": 0
    }
}

As you can see, the tag has one numeric property - p1. The value of the property has been read from the corresponding device and appears to be 0.

Basic Settings

First let’s describes the most essential settings of a tag.

name

A name is used to uniquely identify a tag and define it’s HTTP URL. For instance, the value of a tag named foo would be located at http://localhost:9000/tags/foo

Slashes “/” are useful to represent a logical group of tags. For example, a tag named foo/bar, would have it’s URL as http://localhost:9000/tags/foo/bar

A tag name must be unique. Tag names are not case-sensitive, i.e. foo is equal to FOO.

Type: string

Supported values: any unique string consisting of characters a-z, 0-9, -, /

device

A device on which the tag’s data is located.

This property must contain one of the devices names defined in network.json.

Type: string

Supported values: any string

properties

A list of tag’s properties.

Each property is mapped to an address or a range of addresses of of the device referenced in the device setting.

Each property defines it’s name and data type.

All properties of a tag get read/written simultaneously. They are always included in the tag value object returned to the client.

A tag can have any number of properties. [^1] For instance, here’s a tag with two properties - p1 and p2.

{
    "name": "tag1", "device": "device1", 
    "properties": [
      { "name": "p1", "address": "HR0", "type": "numeric", "raw": "int16" },
      { "name": "p2", "address": "C1", "type": "bool", "raw": "bool" },
    ]
}

Type: array of properties

properties.name

Defines the name of the property as it will appear in the API response object.

For example:

{ "name": "p1", "address": "HR0", "type": "numeric", "raw": "int16" }

Here’s the corresponding API model:

{
    "data": { 
        "p1" : "1",
    }        
}

Each property name has to be unique within it’s tag. Tag names are not case-sensitive, i.e. foo is equal to FOO.

Type: string

Supported values: a unique string consisting of characters a-z, 0-9, _ and starting with a letter

properties.address

A Modbus address of the corresponding data location on the device.

Unserver will read/write to this address when clients get/set the property’s value.

Type: string

Supported values: refer to property addressing

properties.type (optional)

The data type of the property, as it appears in the API response object.

For example the p1 property will be returned as a number, while p2 will be a bollean value.

[
    { "name": "p1", "address": "HR0", "type": "numeric", "raw": "int16" },
    { "name": "p2", "address": "C0", "type": "bool", "raw": "int16" }
]

API response model:

{
    "data": { 
        "p1" : "1",
        "p2" : "False",
    }        
}

The type is optional since version 1.5. If not specified, Unserver will choose either numeric or bool based on Modbus address of the property.

Where needed, Unserver performs data type conversion in order to set/return the correct data type.

Suppose we change the type of p1 from numeric to bool:

[
    { "name": "p1", "address": "HR0", "type": "bool", "raw": "int16" },
    { "name": "p2", "address": "C0", "type": "bool", "raw": "int16" }
]

In this case Unserver will convert the value of HR0 to a boolean value and return the property of specified data type:

{
    "data": { 
        "p1" : "True",
        "p2" : "False",
    }        
}

Type: string

Supported values: numeric, bool

properties.raw (optional)

Specifies the type of raw data located at the property address.

Unserver uses this setting to determine:

  • How many addressess to read/write for a given property
  • How to interpret the values of addresses

For example, when raw is set to int16 and address is HR0, Unserver will only read one location (HR0) in order to determine the property’s value.

But if we change raw to int32, then Unsrever will have read HR0 and HR1 in order to get enough bytes of data and extract a 32-bit number.

Some raw data types have the same length, but different meaning. For example, if we change int16 to bcd16, then Unserver will still only read one address - HR0, but it will interpret the value differently.

The raw setting is optional since version 1.5. If not specified, Unserver will choose either int16 or bool based on Modbus address of the property.

Type: string

Supported Values: int16, int32, uint16, uint32, float32, bcd16, bool

Advanced Settings

Caching

By default, a tag’s value is read from the device at every API read request.

Sometimes this is unnecessary, for example when requests for the same tag arrive frequently but the data changes rarely.

Caching allows to improve efficiency by storing and re-using tag data for a period of time after it has been read from the device.

cache

Specifies the configuration of caching at tag level:

{
    "name": "cached-tag", "device": "device1",
    "cache": { "enabled": true, "TTL": 1000 },
    "properties": [ ... ]
}

This setting is optional.

Type: object

cache.enabled

Must be set to true to enable caching of the tag. To disable caching you can either set cache.enabled to false:

{
    "name": "cached-tag", "device": "device1",
    "cache": { "enabled": false, "TTL": 1000 },
    "properties": [ ... ]
}

Or simply remove the whole cache configuration object:

{
    "name": "cached-tag", "device": "device1",
    "properties": [ ... ]
}

Type: bool

Supported values: true, false

cache.TTL

Time to live specifies how long (in milliseconds) the tag will be served from cache before it’s considered expired.

After TTL expiration the tag will be read from the device again on first request.

With this configuration, the tag is cached for 1 second:

 {
    "name": "caching-demo", "device": "device1",
    "cache": { "enabled": true, "TTL": 1000 },
     "properties": [...]
 }

Polling

For some tags it can be useful to update the cache automatically before it expires. This is useful to reduce the latency of tag read. This option is controlled by the polling setting.

Polling has lower priority than regular (client-initiated) requests.

polling

Specifies the configuration of polling at tag level:

{
    "name": "polling-demo", "device": "device1",
    "polling": { "enabled": true, "frequency": 1 },
    "properties": [ ... ]
}
polling.enabled

Must be set to true to enable polling of the tag.

To disable caching you can either set polling.enabled to false or remove the whole polling configuration object.

Type: bool

Supported values: true, false

polling.frequency

Controls the frequency of cache updates in Hz (updates per second).

This configuration will update the tag’s value at least once every second:

{
    "name": "polling-demo", "device": "device1",
    "polling": { "enabled": true, "frequency": 1 },
    "properties": [ ... ]
}

While you can specify any polling frequency, the actual frequency might be limited by the network load and bandwidth.

Property Addressing

There are three ways you can specify a Modbus address of a property:

  • relative
  • zero-based relative
  • absolute

Examples and the documentation on this site usually use zero-based addressing. But you can choose a different scheme if that’s more convenient.

Absolute: #000001

In this scheme an address looks like this: #000001. It always starts with a # followed by a 5-digit number.

The first digit signifies the type of the address:

  • 0 = coils
  • 1 = discrete inputs
  • 3 = input registers
  • 4 = holding registers

The last 5 digits are the address of the memory location. Addresses start from one, not zero. For example, the address of the first holding register would be #400001.

Relative: C:1

A relative address consists of a data type prefix and a memory address separated by a colon symbol: C:1.

Data types include:

  • C = coils
  • DI = discrete inputs
  • IR = input registers
  • HR = holding registers

Memory addresses start from one, not zero. For example, HR:1 is the address of the first holding register.

Zero-Based Relative: C0

This scheme is similar to “Relative”. It uses the same prefixes for data types, but there’s no “:” between the data type and the address number.

Another difference is the addresses start from zero. For example, HR0 is the address of the first holding register.

The benefit of this addressing scheme is that the values directly correspond to addresses in modbus RTU frames.

API Reference

The main feature of Unserver is the ability to perform read and write operations on data stored in connected devices. In order to do this, Unserver provides a simple and intuitive HTTP API.

This section describes all available API endpoints, as well as their inputs and outputs.

In all API examples we assume that Unserver is using the following (default) root URL: http://localhost:9000/ If you have modified the “api.port” setting in “network.json”, please adjust the root URL accordingly.

Read Tag

To read a tag, perform an HTTP GET request:

http://localhost:9000/tags/{tag-name}

The tag-name should be defined in tags.json.

Successful response example:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "timestamp": "2017-12-01T08:21:26.0796301Z"
    "success": true
    "data":{
        "someProperty":"1"
        "anotherProperty":"2"
    }
}

Write Tag

To write a tag, perform an HTTP PUT request:

http://localhost:9000/tags/{tag-name}

Request model example:

{
    "data":{
        "someProperty":"1"
        "anotherProperty":"2"
    }
}

Successful response example:

HTTP/1.1 204 No Content

Write a Single Property

As opposed to writing the whole tag, it’s possible to write a just a single property. To do this, make a PUT request to:

http://localhost:9000/tags/{tag-name}/properties/{property-name}

Pass the value of the property in the request body:

{
    value:"1"
}

Successful response example:

HTTP/1.1 204 No Content

Unsuccessful Response

When Unserver is running correctly, but is unable to complete a request, it will return the following response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "success": false,
    "error": "error message"
}

The error property should contain the explanation of what went wrong.

Command Line

Specifying Configuration Files

When you run unserver.exe without additional arguments, it uses configuration files named network.json and tags.json.

You can also specify different configuration files, for example:

unserver.exe -t:tagfile.json -n:networkfile.json

Managing Unserver Background Service

The following commands are used to manage the Unserver service.

unserver.exe install
unserver.exe uninstall
unserver.exe start
unserver.exe stop

On Windows 7 and up you must run the commands as Administrator.

Working with Multiple Service Instances

You can run multiple instances of Unserver background service.

To install and manage instances, add the -instance option to commands:

unserver.exe install -instance:"1"
unserver.exe install -instance:"2"
unserver.exe stop -instance:"1" 
unserver.exe uninstall -instance:"1" 

In this example we install two Unserver instances: 1 and 2. Then we stop instance 1 and uninstall it, while leaving instance 2 running.

Troubleshooting

This section describes solutions to possible issues you might encounter while running Unserver.

Before trying to diagnose an issue, it’s best to get as much information as necessary. To do this, enable logging.

Logging Settings

If you are using Unserver as a console app, the logs will be written to the console.

When running as a background service, there are two options:

  • Stop the service and run Unserver as a console app for the time of diagnostics
  • Enable logging to file (see below)

Logging to File

When logging to file is enabled, Unserver will save logs to a file named unserver.log. To enable logging to file, open logging.json and set the file option to true.

Logging Levels

If something doesn’t work as expected and you don’t see any error messages in the logs, make sure the level setting is set to INFO in logging.json.

Common Errors

Startup Errors

Startup errors occur when some of the configuration is incorrect. Some common causes for startup issues are:

  • Corrupted configuration file(s): network.json, server.json
  • Inability to open the serial port specified in network.json
  • Inability to bind to the HTTP endpoint because the selected TCP port is busy

In such cases Unserver will typically output an error message detailed enough to identify and fix the issue.

To solve the issue, review the console output or the unserver.log file and correct the configuration.

No Response from Unserver

If you are trying to call Unserver HTTP API and are not getting any response, the possible reasons may be:

  • using a wrong TCP port
  • improper firewall configuration
  • background service is not running
Using a Wrong TCP Port

Read API port configuration and make sure your configuration is correct.

Firewall is not Configured Properly

Follow the instructions on firewall configuration.

Background Service is Not Running

If you are trying to use the Unserver background service, make sure it’s installed and started.

To check if the service is installed and running:

  1. press Windows+R
  2. type services.msc
  3. press Enter
  4. find Unserver in the list of services and check it’s status

404 Page Not Found Response

This is may be caused by one of the following:

The Requested Tag or Property Don’t Exist
  1. Make sure the tag is defined in tags.json.
  2. Make sure you restart Unserver after you edit tags.json.
Calling Invalid API URL

Review HTTP API reference and make sure you are constructing tag URLs correctly.

Communication Timeouts

Sometimes you may get Unserver response like this:

{
    success:"false",
    message:"timeout while communicating with device"
}

It means that Unserver is running correctly, but it was unable to communicate with the target field device. Possible reasons:

Invalid Serial Port Configuration

Configuration of Unserver serial port doesn’t match the parameters of Modbus network.

To solve the issues, review serial port parameters in network.json. Make sure the Modbus device you are using is configured in the same way.

Invalid Slave Id

Address of the device specified in network.json is different from the actual Modbus Slave Id of the device.

To solve the issues, review devices in network.json and make sure the address of the target device is correct.

Physical Connection Issues

Sometimes the serial port Unserver uses is not connected to the target device properly:

  • the cable between Unserver PC and the target device/network has invalid pin layout
  • the cable is damaged
  • serial port of Unserver PC is not functioning

To solve the issue:

  1. Test the cable using a multimeter. Use manuals for your hardware devices to determine the correct pin layout. Make sure to cross-over Rx and Tx.
  2. Connect the Rx and Tx pins of the serial port used by Unserver. Then, using any terminal application, send some characters by the port. If the port is working properly, the characters should be echoed back and appear in the terminal.

Modbus Address is not Supported by the Device

Another condition a timeout may occur, is when a target device lacks a specific address or doesn’t support a function code. In this case the device might simply ignore the request, resulting in a timeout error.

Solution: - Review the manual to the target device and make sure the specified address exists - Find out which function codes are not supported by the device and specify unsuported function codes

Unexpected Tag Value in the Response

The value returned by Unserver might be different from what you expect it to be. Possible reasons for that:

  • Addressing scheme used in Unserver configuration is different from the one used by the target device
  • The tag value is cached for too long so it is served outdated

To solve this issue: - Review property addressing and make sure you are using the same addressing scheme for Unserver configuration and for verification of actual tag data - Review tags.json and adjust the target tag’s caching configuration if necessary