Modbus¶
Note
Modbus protocol follows the master / slave principle. Modbus features offered by OnSphere are Master features.
Capabilities¶
Capabilities |
Support |
Comment |
---|---|---|
Modbus slave |
This feature is currently not supported. In case of interest please contact us at info@swissdotnet.ch |
|
Modbus master |
||
Modbus RTU |
It’s necessary to use a physical device (ex: MOXA for converting protocols). |
|
Modbus TCP |
||
Slave connectivity monitoring |
||
Read |
See Reading |
|
Write |
See Writing |
|
Data conversion |
See data conversion for read and write. |
|
Multi-register usage |
Read/write value spanning over multiple Modbus registers. See multi-register usage. |
|
Sub-ranges |
Allows to select some bits of a register to read / write. See sub ranges. |
Type of values¶
Type |
Support Read |
Support Write |
Comment |
---|---|---|---|
BOOLEAN |
|||
INT8 |
See sub ranges |
||
UINT8 |
See sub ranges |
||
INT16 |
|||
UINT16 |
|||
INT32 |
|||
UINT32 |
|||
INT64 |
|||
UINT64 |
This only support for a value up to 263-1 or 9,223,372,036,854,775,807. |
||
FLOAT32 |
|||
ASCII |
|||
BITMAP |
|||
DATETIME |
Examples¶
Concept¶
OnSphere allows you to easily read and write data on a Modbus slave without being bothered with conversion between high level abstracted data you want to read / write (e.g string, signed integer, floating value …) and what the slave will understand (bits). It even go a step further since it extends its mechanism converting what you need to what the slave is expecting to partial Modbus register as well as multi-register. This means you can read an 8 bits unsigned integer on an Input register or write a float on consecutive Holding registers as easily as you can get a boolean out of a Coil.
Since it is often useful to know the “state” of a slave OnSphere also provides a feedback on slaves connectivity.
A lot of parameters have been made available to allow you to best tailor OnSphere behavior to your needs and those of the devices you are interacting with.
Note
While Modbus protocol defines many requirements a lot of choices have been left to manufacturers. It is important to keep this in mind as you might encounter devices with constraints which might differ from what you are used to. Below is a list of some of those aspects:
Not all slaves have all four different tables (coil, discrete input, input register and holding register). Only the slave documentation should be used to determine which tables it provides
Not all slaves use system reserved port 502. Only the slave documentation should be used to determine out of factory configured port.
Not all slaves use 0 based address (OnSphere does, but other devices have addresses starting a 1).
Use-case¶
OnSphere will read/write anything a slave allows it to, which means there are an infinite of usages
Monitor server room temperature
Open gate
Count people passing through a door
Control a room lighting
…
Examples¶
Connection on slave¶
First step once your osp-modbus
module is deployed is to configure it to connect on a slave.
Some slaves only allow one connection at a time (concurrentConnections
), others limit the number of values which can be read/written in a single request (bitMaxSize
/ registerMaxSize
) or even the number of requests which can be done in a given time frame. To be able to match this kind of constraints OnSphere makes a lot of parameters available.
Slave connectivity monitoring¶
OnSphere publishes a value whose content reflects the connectivity state of the slave it is associated with: True
when the connection with the slave is established, False
otherwise.
For slave with multiple concurrentConnections configured :
True
when the first connection on the slave is successfully establishedFalse
when the last connection on the slave is lost
Warning
Monitoring of slave connectivity is only done when at least one value is read. When there is no configuration to read any value, slave connectivity is reported as False
if write fails. It means if you really need a quick feedback on slave connectivity then you should configure your slave with a short readPeriod and at least one value to read.
Reading¶
Note
OnSphere handles reading periodically. Every changes in-between readings is lost.
OnSphere publishes the content of values it reads on its communication bus for other modules to use. If a value does not change, nothing new is published on the communication bus. It is also possible to provide a delta parameter to indicate new value should be published only if it changed more than this provided delta.
Values are read periodically with a readPeriod configured at slave level. This means if you have different refresh rate needs from one value to another on the same slave you should either configure the slave with the highest readPeriod
or, if the slave allows it, create two slaves configuration both for the same end device (with same host parameter) but with different readPeriod
.
Writing¶
While reading is done periodically, writing on the other hand is on demand.
OnSphere allows Modbus writing through callbacks
: define your write and simply reference it in a callback.
OnSphere handle the following errors :
content to write not matching configured
modbusType
(e.g request to write a number on acoil
)content to write not matching configured
valueType
(e.g request to write 70000 on anINT16
is out of range)content to write not properly formatted (e.g missing field, not json compliant, …)
Note
If a writing fails, OnSphere will retry every 2s during 20s
Data conversion¶
OnSphere handles data conversion on its own so that you can focus on what you want to read/write instead of how you should read/write it. Simply specify the valueType
you want to read / write and OnSphere will do the rest.
Note
OnSphere is able to detect invalid valueType
/ modbusType
combination (e.g trying to read a FLOAT
spanning on two register out of a single bit COIL
) for you and report it before the configuration is applied.
Both read and write expose an endianness
parameter. Despites its name, the endianness
parameters allows to set the endianness to use as well the the bytes ordering.
Warning
The device manufacturer documentation is the ONLY source of truth. The valueType
you can expect the slave to provide, the modbusType
it is stored in or the endianness
used to store it are all at the manufacturer discretion.
Multi-register¶
Modbus registers are 16 bits registers which means by default, Modbus does not handle FLOAT
, DOUBLE
, UINT32
or any value needing more than one register. Fortunately, OnSphere does not have such restriction. Simply specify the valueType
and OnSphere will do the rest.
Sub-ranges¶
Sub-ranges are provided through a combination of offset
and binary AND mask. The offset is applied first to discard unwanted bits then the mask to select needed bits among the remaining ones (those not discarded by the offset
).
Warning
The mask
is a binary AND mask but it must be configured in decimal. With AND mask, to keep a bit you set your mask to 1 and to 0 to discard it. Let’s say you want to keep 2 bits, mask is 1
1
which in the configuration should be set to 3
(11 in binary is 3 in decimal)
Note
Reading 8 bits
valueType
requires sub-ranges since they do not occupy a full Modbus register. If none is provided a default one is used to read only the first part of the register (offset
: 0 /mask
: 255)
The offset is applied first, then the mask. This is always true and should be enough to keep in mind most of the time. But, for more complicated usage, know that the complete pipeline is as follow :
raw bytes -> valueType -> offset -> mask -> ToType (where TOType is the type configured in the associated value.ospp
Examples¶
Defining offset
and mask
to get the needed sub-range can be a bit daunting at first. To help you better understand how to define those here are some examples of sub-ranges usage to read part of a Modbus register.
Most common usage will most likely be reading a single bit of an UINT16
valueType
which can easily be done with a 1 mask
and an offset
used as an index (starting at 0) e.g :
Given a Modbus register (16 bits) containing the following value : 0 0 0 0 0 0 0 0 | 1 0 1 1 0 1 0 1
Index |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
e.g val |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
Note
Index must be read from right to left to match default BIG endian endianness (most significant bit stored first)
To help understanding following example, bits discarded by offset are replaced by a
X
while those selected by the mask are int in bold
So, let’s say we want 3 sub-ranges allowing to get :
first bit (at index 0, whose value here is 1)
fourth bit (at index 3, whose value here is 0)
sixth bit (at index 5, whose value here is 1)
Sub-range for the first bit¶
Index starts at 0 so first bit index is 0 which gives us offset
: 0. To select mask
: 1
Index |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
decimal value |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
e.g val |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
181 |
offset |
0 |
||||||||||||||||
mask |
1 |
1 |
|||||||||||||||
mask selection |
1 |
||||||||||||||||
res |
1 |
1 |
-> (offset
: 0, mask
: 1) result value : 1
Sub-range for the fourth bit¶
Index starts at 0 so fourth bit index is 3 which gives us offset
: 3. To select mask
: 1
Index |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
decimal value |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
e.g val |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
181 |
offset |
X |
X |
X |
3 |
|||||||||||||
mask |
1 |
1 |
|||||||||||||||
mask selection |
0 |
||||||||||||||||
res |
0 |
0 |
-> (offset
: 3, mask
: 1) result value : 0
Sub-range for the sixth bit¶
Index starts at 0 so sixth bit index is 5 which gives us offset
: 5. To select mask
: 1
Index |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
decimal value |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
e.g val |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
181 |
offset |
X |
X |
X |
X |
X |
5 |
|||||||||||
mask |
1 |
1 |
|||||||||||||||
mask selection |
1 |
||||||||||||||||
res |
1 |
1 |
-> (offset
: 5, mask
: 1) result value : 1
Now let’s see a few examples selecting more than 1 bits :
Sub-range for first three bits¶
Index starts at 0, we want the first
three bits which gives us offset
: 0. The sub-range mask
is a binary AND mask
. With an AND mask, to keep bits, bits of mask but be set to 1. We want to keep three bits so : 1 1 1 which gives us a mask
of 7
Index |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
decimal value |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
e.g val |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
181 |
offset |
0 |
||||||||||||||||
mask |
1 |
1 |
1 |
7 |
|||||||||||||
mask selection |
1 |
0 |
1 |
||||||||||||||
res |
1 |
0 |
1 |
5 |
-> (offset
: 0, mask
: 7) result value : 1 0 1 (5 in decimal)
Sub-range for second and third bits¶
Index starts at 0, first bit we want to keep is the second one which gives us offset
: 1. We want to keep two bits so : 1 1 which gives us a mask
of 3
Index |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
decimal value |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
e.g val |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
181 |
offset |
X |
1 |
|||||||||||||||
mask |
1 |
1 |
3 |
||||||||||||||
mask selection |
1 |
0 |
|||||||||||||||
res |
1 |
0 |
2 |
-> (offset
: 1, mask
: 3) result value : 1 0 (2 in decimal)
Sub-range for fourth and seventh bits¶
Index starts at 0, first bit we want to keep is the fourth one which gives us offset
: 3. We want to keep (1) the fourth bits, discard (0) fifth and sixth and keep the seventh bit so : 1 0 0 1 which gives us a mask
of 9
Index |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
decimal value |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
e.g val |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
181 |
offset |
X |
X |
X |
3 |
|||||||||||||
mask |
1 |
0 |
0 |
1 |
9 |
||||||||||||
mask selection |
0 |
1 |
1 |
0 |
|||||||||||||
res |
0 |
0 |
0 |
0 |
0 |
-> (offset
: 3, mask
: 9) result value : 0 0 0 0 (0 in decimal)
Requirement¶
Module osp-modbus