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:
Please make sure you PC meets the system requirements.
Download the latest Unserver distributable.
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:
- Open the working directory and launch Unserver by running
unserver.exe
- If a dialog appears, click “Yes”
- Make sure there are no error messages in the console
- 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.
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.
- Open Windows command propt
cmd.exe
as adminstrator
- Navigate to Unserver working directory
- 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:
- Purchase an activation key
- Save the key file to Unserver working directory
- 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
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:
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.
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:
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:
Or you will see an unsuccessful response:
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
links
Type: object array
The links
property is an array of objects, each representing a communication link, for example:
For Modbus RTU communication links, a configuration object looks like this:
For Modbus TCP communication links, a configuration object looks like this:
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
devices.link
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):
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.host
(optional)
A hostname or IP used by Userver HTTP API.
If you don’t specify this setting, the API will listen on all system IP addresses. This works fine most of the time.
To just bind to a single IP, use this setting:
Make sure you don’t have a Url reservation added for +:9000
, otherwise it’s possible to get 503 errors when trying to call the API. You can remove existing reservations with this command:
netsh http delete urlacl http://+:9000/
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.
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:
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:
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:
Here’s the corresponding API model:
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.
API response model:
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
:
In this case Unserver will convert the value of HR0
to a boolean value and return the property of specified data type:
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:
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
:
Or simply remove the whole cache
configuration object:
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:
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:
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:
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:
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:
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.
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:
- press Windows+R
- type
services.msc
- press Enter
- 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
- Make sure the tag is defined in
tags.json
.
- 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:
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:
- 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
.
- 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