Kelvin Modbus Bridge App¶
Modbus is a serial communication protocol developed by Modicon for use with its programmable logic controllers (PLCs). Modbus is often used to connect a supervisory computer with a remote terminal unit (RTU) in supervisory control and data acquisition (SCADA) systems. Modbus is transmitted over serial lines between devices.
The Kelvin Modbus Bridge App is a Kelvin App runs on KICS. It is a data connector application that connects and communicate with devices running Modbus protocol.
Prerequisite:
- Complete the Getting Started Guide
Retrieving the application¶
The application should be available on your platform's Application Registry. We can search the application registry with the command:
kelvin appregistry search modbus
[kelvin.sdk][2021-07-06 20:04:54][I] Searching applications that match "modbus"
[kelvin.sdk][2021-07-06 20:04:56][I]
*************************** Applications ***************************
+-----------------------------+------------------------------------+--------+------------------+----------------------------------+
| Name | Title | Type | Latest Version | Updated |
|-----------------------------+------------------------------------+--------+------------------+----------------------------------|
| kelvin-bridge-modbus-client | Kelvin Modbus Protocol Application | bridge | 2.0.7 | 2021-07-01 14:37:23.937756+00:00 |
+-----------------------------+------------------------------------+--------+------------------+----------------------------------+
In this case we will be using the application named
kelvin-bridge-modbus-client and the latest version is 2.0.7.
Note
Use the app version found in your app registry list.
kelvin appregistry download kelvin-bridge-modbus-client:2.0.7
[kelvin.sdk][2021-07-06 20:16:49][I] Downloading application "kelvin-bridge-modbus-client:2.0.7"
[kelvin.sdk][2021-07-06 20:16:54][R] Successfully logged on registry "alpha.kelvininc.com:5000"
[kelvin.sdk][2021-07-06 20:16:54][I] Pulling "kelvin-bridge-modbus-client:2.0.7" from "alpha.kelvininc.com"
...
[kelvin.sdk][2021-07-06 20:17:01][R] Successfully pulled "kelvin-bridge-modbus-client:2.0.7" from "alpha.kelvininc.com"
[kelvin.sdk][2021-07-06 20:17:01][R] Application "kelvin-bridge-modbus-client:2.0.7" successfully downloaded to the local registry
[kelvin.sdk][2021-07-06 20:17:01][R] Use `kelvin app images unpack` to extract its contents
As suggested by KSDK, let's unpack the image. The image name follows the rule
<registry address>/<app name>:<version>, but the best way to find out is by
using KSDK to list the available images.
ksdk app images list
[kelvin.sdk][2021-07-06 20:18:07][I]
*************************** Existing Apps ***************************
+------------------------------------------------------------+---------------------+
| Applications | Created |
|------------------------------------------------------------+---------------------|
| alpha.kelvininc.com:5000/kelvin-bridge-modbus-client:2.0.7 | 2021-07-01 15:37:03 |
+------------------------------------------------------------+---------------------+
Now that we know the app name, we can unpack it to a directory of our choosing.
kelvin app images unpack alpha.kelvininc.com:5000/kelvin-bridge-modbus-client:2.0.7 modbus
[kelvin.sdk][2021-07-06 20:19:50][I] Unpacking application "kelvin-bridge-modbus-client:2.0.7" to directory "modbus"
[kelvin.sdk][2021-07-06 20:19:53][R] Application "kelvin-bridge-modbus-client:2.0.7" successfully unpacked to "modbus"
You should now have the application unpacked to the directory modbus/ and we
can proceed to its configuration.
Configuration¶
The configuration for the whole application and deployment sits on the
app.yaml file.
Metrics mapping¶
The metrics map will map the Modbus addresses to the inputs and outputs of the application.
Each metric has the following general configurations:
| Name | Description | Default/Required |
|---|---|---|
name |
The metric name | Required |
asset_name |
The asset name associated to the metric name | Required |
data_type |
The data type of the metric | Required |
access |
Whether the metric is RO (read-only) or RW (read-write) |
Default: RO |
| Name | Applicable to | Description |
|---|---|---|
protocol_type |
All | The primitive data type |
address |
All | The Modbus' register address to be read |
polling_rate |
All | The register polling period |
stroke_number |
Stroke number | Indicates the address is the stroke number |
buffer_address |
Downhole/surface | The first address of the buffer |
buffer_size |
Downhole/surface | The size of the buffer |
Example
Below is an example of a Modbus Bridge application's metrics_map configuration
for dynacards, the one present in the downloaded configuration file.
app:
bridge:
# ...
metrics_map:
- name: stroke_number
asset_name: modbus_simulator
data_type: raw.uint32
configuration:
address: 2518
protocol_type: uint32
polling_rate: 15
stroke_number: true
- name: downhole
asset_name: modbus_simulator
data_type: raw.dynacard
configuration:
protocol_type: downhole
polling_rate: 15
buffer_address: 46165
buffer_size: 200
- name: surface
asset_name: modbus_simulator
data_type: raw.dynacard
configuration:
protocol_type: surface
polling_rate: 15
buffer_address: 45756
buffer_size: 400
General¶
General configuration related to the Modbus connection.
| Name | Description |
|---|---|
minus_offset |
Modbus register offset (1 is the most commonly used value) |
timestamp_sync |
Timestamp synchronization across all metrics read within the same polling period (true or false) |
chunk_size |
Maximum amount of registers to read per request (125 for Serial and 123 for TCP) |
debug |
libmodbus debug logs (true or false) |
Example
app:
bridge:
configuration:
minus_offset: 0
chunk_size: 120
timestamp_sync: false
debug: false
connection:
# ...
Connection¶
Modbus connection configuration that can be applied to both the TCP and serial connection.
Common¶
| Name | Description |
|---|---|
type |
Establish the connection to ModBus via "tcp" or "serial" |
slave_id |
Slave ID of the remote device to talk in Master mode (1-255) |
timeout |
Timeout interval (in seconds) used to wait for a response |
reconnect_delay |
Delay (in seconds) before attempt to reconnect to the Modbus Slave |
retry_attempts |
Read requests maximum retry attempts |
retry_delay |
Delay (in seconds) between read requests retry attempts |
Example
app:
bridge:
configuration:
# ...
connection:
slave_id: 255
timeout: 5
reconnect_delay: 15
retry_attempts: 0
retry_delay: 0
type: tcp
# ...
TCP¶
To connect to Modbus via TCP, the connection type must be tcp.
| Name | Description |
|---|---|
ip |
IP address of the server to which the client wants to establish a connection |
port |
Port to use (Modbus TCP default is 502) |
The ip field can be an IP address as well as an host name. Inside both KICS
and KSDK's emulation environment, that host name may point to another running
application. That name is <app-name/workload-name>.app.
Example
app:
bridge:
configuration:
connection:
# ...
type: tcp
tcp:
ip: kelvin-modbus-simulator.app
port: 502
Serial¶
To connect to Modbus via TCP, the connection type must be serial.
| Name | Description |
|---|---|
serial_port |
Name of the serial port handled by the OS, i.e. /dev/ttyS0 or /dev/ttyUSB0 |
baudrate |
Baud rate of the communication, i.e. 9600, 19200, 57600, 115200, etc |
data_bits |
Number of bits of data, the allowed values are 5, 6, 7 and 8 |
parity |
N for none, E for even, and O for odd |
stop_bits |
The bits of stop, the allowed values are 1 and 2 |
serial_mode |
Serial line communication, RS232 and RS485 |
rts_mode |
(RS485 only) Mode to communicate on a RS485 serial bus, UP (default) or DOWN |
rts_delay |
(RS485 only) Request to send delay period (in seconds) |
error_recovery |
If set to true, it will attempt to reconnect when disconnected (not recommended for slave mode) |
Example
app:
bridge:
configuration:
connection:
# ...
type: serial
serial:
serial_port: /dev/ttyXRUSB1
baudrate: 19200
parity: N
data_bits: 8
stop_bits: 1
Building and testing the application¶
Every time we build an application, we need to bump its version so that the server knows we have a different image.
Info
Because this is a pre-built application, there is no need to build and upload it unless we want to change the default configuration. In most cases there's only need to deploy the new configuration.
In our example, let's bump it to 2.1.0.
Example
spec_version: 2.0.0
info:
name: kelvin-bridge-modbus-client
title: Kelvin Modbus Protocol Application
version: 2.1.0
Additionally, to test the actual communication, we'll be using a Modbus Simulator application that allows us to connect this application to it. The default configuration is already prepared to connect to the simulator.
We can do that by downloading and starting the application like so:
ksdk appregistry download kelvin-modbus-simulator:1.2.0 \
&& ksdk emulation start alpha.kelvininc.com:5000/kelvin-modbus-simulator:1.2.0
Tip
Remember that you can list the downloaded images by executing kelvin app images list.
Warning
On Windows systems, the simulator does not work when using Docker with the WSL 2 backend.
To build the application, we use KSDK.
ksdk app build
[kelvin.sdk][2021-07-06 22:46:30][I] Assessing basic application info..
[kelvin.sdk][2021-07-06 22:46:30][I] Valid schema available locally. Using cached version (~/.config/kelvin/schemas/2.0.0.json)
[kelvin.sdk][2021-07-06 22:46:32][I] Building "Bridge type" application "kelvin-bridge-modbus-client"
[kelvin.sdk][2021-07-06 22:46:33][I] Processing application data types..
[kelvin.sdk][2021-07-06 22:46:33][I] Loading all data type files from directory "~/.config/kelvin/datatypes"
[kelvin.sdk][2021-07-06 22:46:33][R] 13 data types loaded from directory "~/.config/kelvin/datatypes"
[kelvin.sdk][2021-07-06 22:46:33][I] Retrieving data types for "kelvin-bridge-modbus-client"
[kelvin.sdk][2021-07-06 22:46:34][R] Successfully logged on registry "alpha.kelvininc.com:5000"
[kelvin.sdk][2021-07-06 22:46:34][I] Pulling "alpha.kelvininc.com:5000/kelvin-core-cpp:7.0.2-aeaba1b" from "alpha.kelvininc.com"
[kelvin.sdk][2021-07-06 22:46:36][R] Successfully pulled "alpha.kelvininc.com:5000/kelvin-core-cpp:7.0.2-aeaba1b" from "alpha.kelvininc.com"
[kelvin.sdk][2021-07-06 22:46:36][I] Pulling "alpha.kelvininc.com:5000/data-model-builder:6.0.1-081bf89" from "alpha.kelvininc.com"
[kelvin.sdk][2021-07-06 22:46:37][R] Successfully pulled "alpha.kelvininc.com:5000/data-model-builder:6.0.1-081bf89" from "alpha.kelvininc.com"
[kelvin.sdk][2021-07-06 22:46:37][I] Building new image for "kelvin-bridge-modbus-client:2.0.7". Please wait..
[kelvin.sdk][2021-07-06 22:47:16][R] Image successfully built: "kelvin-bridge-modbus-client:2.0.7"
Once the application is built, we can start it locally in an emulation environment:
$ kelvin emulation start --app-config app.yaml --show-logs
Tip
You can stop following the emulation logs with Ctrl+C.
If the application connects to the Modbus Simulator, it should show a log from the storage system for each emitted metric, and can also show logs from the bridge app itself:
Success
[kelvin.sdk][2021-07-06 22:27:34][R] Emulation configuration loaded from: "app.yaml"
[kelvin.sdk][2021-07-06 22:27:35][I] Valid schema available locally. Using cached version (~/.config/kelvin/schemas/2.0.0.json)
[kelvin.sdk][2021-07-06 22:27:38][R] Kelvin Emulation System is online
[kelvin.sdk][2021-07-06 22:27:38][I] Loading configuration and starting the application "kelvin-bridge-modbus-client:2.0.7"
[kelvin.sdk][2021-07-06 22:27:38][I] Valid schema available locally. Using cached version (~/.config/kelvin/schemas/2.0.0.json)
[kelvin.sdk][2021-07-06 22:27:40][R] Overriding existing configuration with "app.yaml"
[kelvin.sdk][2021-07-06 22:27:41][R] Application successfully launched: "kelvin-bridge-modbus-client:2.0.7"
[ApplicationEngine.cpp: 26:I] - Loading configuration file: /opt/kelvin/app/app.yaml
[ Pipeline.cpp: 73:I] - Loading shared object: kelvin_bridge_modbus_client/kelvin_bridge_modbus_client.so (kelvin-bridge-modbus-client) - SUCCESS
[ Pipeline.cpp: 188:I] - Loading kelvin-bridge-modbus-client Refinery input modules:
[ Pipeline.cpp: 193:I] - Loading kelvin-bridge-modbus-client Refinery output modules:
[ Pipeline.cpp: 198:I] - Loading kelvin-bridge-modbus-client Gatekeeper input modules
[ Pipeline.cpp: 203:I] - Loading kelvin-bridge-modbus-client Gatekeeper output modules
[ StorageManager.cpp: 27:I] - Using eXtremeDB storage backend
[ExtremeDbBackend.cpp: 277:I] - [config] Commit policy: MCO_COMMIT_SYNC_FLUSH // Transaction log type: REDO_LOG
[ExtremeDbBackend.cpp: 318:I] - [config] Dev: 0 // Memory type: MCO_MEMORY_CONV // Assignment: MCO_MEMORY_ASSIGN_DATABASE // Size: 2097152 // Flags: MCO_FILE_OPEN_DEFAULT
[ExtremeDbBackend.cpp: 318:I] - [config] Dev: 1 // Memory type: MCO_MEMORY_CONV // Assignment: MCO_MEMORY_ASSIGN_CACHE // Size: 8388608 // Flags: MCO_FILE_OPEN_DEFAULT
[ExtremeDbBackend.cpp: 318:I] - [config] Dev: 2 // Memory type: MCO_MEMORY_FILE // Assignment: MCO_MEMORY_ASSIGN_PERSISTENT // Filename: ./storage/kelvindb_v3.dbs // Flags: MCO_FILE_OPEN_DEFAULT
[ExtremeDbBackend.cpp: 318:I] - [config] Dev: 3 // Memory type: MCO_MEMORY_FILE // Assignment: MCO_MEMORY_ASSIGN_LOG // Filename: ./storage/kelvindb_v3.log // Flags: MCO_FILE_OPEN_DEFAULT
[ExtremeDbBackend.cpp: 258:I] - eXtremeDB is running in PAID LICENSE MODE
[ExtremeDbBackend.cpp: 260:I] - eXtremeDB version: interim build eXtremeDB_8.1_1800, rev.25973
[ExtremeDbBackend.cpp: 30:I] - eXtremeDB started successfully.
[ UploadManager.cpp: 28:I] - The UploadManager's worker will not start because it's either disabled or has no address configured
[ Poller.cpp: 70:I] - Successful poller: 0.100000 secs (fd: 7)
[ DataModel.hpp: 89:I] - kelvin-bridge-modbus-client initialize.
[ Poller.cpp: 70:I] - Successful poller: 1.000000 secs (fd: 8)
[ Poller.cpp: 70:I] - Successful poller: 10.000000 secs (fd: 9)
[ Poller.cpp: 70:I] - Successful poller: 15.000000 secs (fd: 10)
[kelvin_bridge_modbus_client.cpp:1014:I] - Reading polling group starting with address 2518 of size 1
[kelvin_bridge_modbus_client.cpp:1084:I] - Reading dynacards associated with stroke number register 'stroke_number'
[ExtremeDbBackend.cpp: 418:I] - Registering scaled_example in rule-defined layout structure (VERTICAL)
[ExtremeDbBackend.cpp: 418:I] - Registering stroke_number in rule-defined layout structure (VERTICAL)
[ExtremeDbBackend.cpp: 421:I] - Registering surface in default layout structure (HORIZONTAL)
[ExtremeDbBackend.cpp: 421:I] - Registering downhole in default layout structure (HORIZONTAL)
[kelvin_bridge_modbus_client.cpp:1014:I] - Reading polling group starting with address 2518 of size 1
[kelvin_bridge_modbus_client.cpp:1084:I] - Reading dynacards associated with stroke number register 'stroke_number'
[kelvin_bridge_modbus_client.cpp:1014:I] - Reading polling group starting with address 2518 of size 1
[kelvin_bridge_modbus_client.cpp:1014:I] - Reading polling group starting with address 2518 of size 1
[kelvin_bridge_modbus_client.cpp:1084:I] - Reading dynacards associated with stroke number register 'stroke_number'
[kelvin_bridge_modbus_client.cpp:1014:I] - Reading polling group starting with address 2518 of size 1
...
Otherwise, if it fails, it will output errors such as:
Fail
[kelvin.sdk][2021-07-06 22:31:58][R] Emulation configuration loaded from: "app.yaml"
[kelvin.sdk][2021-07-06 22:31:59][I] Valid schema available locally. Using cached version (~/.config/kelvin/schemas/2.0.0.json)
[kelvin.sdk][2021-07-06 22:32:02][R] Kelvin Emulation System is online
[kelvin.sdk][2021-07-06 22:32:02][I] Loading configuration and starting the application "kelvin-bridge-modbus-client:2.0.7"
[kelvin.sdk][2021-07-06 22:32:02][I] Valid schema available locally. Using cached version (~/.config/kelvin/schemas/2.0.0.json)
[kelvin.sdk][2021-07-06 22:32:04][I] Stopping container "kelvin-bridge-modbus-client:2.0.7"
[kelvin.sdk][2021-07-06 22:32:06][R] Overriding existing configuration with "app.yaml"
[kelvin.sdk][2021-07-06 22:32:06][R] Application successfully launched: "kelvin-bridge-modbus-client:2.0.7"
[ApplicationEngine.cpp: 26:I] - Loading configuration file: /opt/kelvin/app/app.yaml
[ Pipeline.cpp: 73:I] - Loading shared object: kelvin_bridge_modbus_client/kelvin_bridge_modbus_client.so (kelvin-bridge-modbus-client) - SUCCESS
[ Pipeline.cpp: 188:I] - Loading kelvin-bridge-modbus-client Refinery input modules:
[ Pipeline.cpp: 193:I] - Loading kelvin-bridge-modbus-client Refinery output modules:
[ Pipeline.cpp: 198:I] - Loading kelvin-bridge-modbus-client Gatekeeper input modules
[ Pipeline.cpp: 203:I] - Loading kelvin-bridge-modbus-client Gatekeeper output modules
[ StorageManager.cpp: 27:I] - Using eXtremeDB storage backend
[ExtremeDbBackend.cpp: 277:I] - [config] Commit policy: MCO_COMMIT_SYNC_FLUSH // Transaction log type: REDO_LOG
[ExtremeDbBackend.cpp: 318:I] - [config] Dev: 0 // Memory type: MCO_MEMORY_CONV // Assignment: MCO_MEMORY_ASSIGN_DATABASE // Size: 2097152 // Flags: MCO_FILE_OPEN_DEFAULT
[ExtremeDbBackend.cpp: 318:I] - [config] Dev: 1 // Memory type: MCO_MEMORY_CONV // Assignment: MCO_MEMORY_ASSIGN_CACHE // Size: 8388608 // Flags: MCO_FILE_OPEN_DEFAULT
[ExtremeDbBackend.cpp: 318:I] - [config] Dev: 2 // Memory type: MCO_MEMORY_FILE // Assignment: MCO_MEMORY_ASSIGN_PERSISTENT // Filename: ./storage/kelvindb_v3.dbs // Flags: MCO_FILE_OPEN_DEFAULT
[ExtremeDbBackend.cpp: 318:I] - [config] Dev: 3 // Memory type: MCO_MEMORY_FILE // Assignment: MCO_MEMORY_ASSIGN_LOG // Filename: ./storage/kelvindb_v3.log // Flags: MCO_FILE_OPEN_DEFAULT
[ExtremeDbBackend.cpp: 258:I] - eXtremeDB is running in PAID LICENSE MODE
[ExtremeDbBackend.cpp: 260:I] - eXtremeDB version: interim build eXtremeDB_8.1_1800, rev.25973
[ExtremeDbBackend.cpp: 30:I] - eXtremeDB started successfully.
[ UploadManager.cpp: 28:I] - The UploadManager's worker will not start because it's either disabled or has no address configured
[ Poller.cpp: 70:I] - Successful poller: 0.100000 secs (fd: 7)
[ DataModel.hpp: 89:I] - kelvin-bridge-modbus-client initialize.
[ Poller.cpp: 70:I] - Successful poller: 1.000000 secs (fd: 8)
[ Poller.cpp: 70:I] - Successful poller: 10.000000 secs (fd: 9)
[ Poller.cpp: 70:I] - Successful poller: 15.000000 secs (fd: 10)
[kelvin_bridge_modbus_client.cpp: 581:E] - # Modbus Connection ERROR # Connection refused
[kelvin_bridge_modbus_client.cpp: 582:E] - # Modbus # Retrying in 15 seconds...
...
Tip
As mentioned above, you don't need to rebuild the application when you change the configuration. If you want to emulate the application with a different configuration, you can use:
kelvin emulation start --show-logs --app-config app.yaml
Once all your tests are completed and you are happy with the result, you can stop the emulation with:
kelvin emulation stop