ModBus routing gateway with pluggable frontends/backends and MQTT mirroring
Project description
Modbus Gateway
A flexible and extensible ModBus gateway written in Python, supporting pluggable frontends and backends, advanced routing, and security filtering.
The gateway allows to bridge, transform, and secure ModBus communication across heterogeneous systems - from serial devices to TCP and Unix domain sockets - while maintaining full control over addressing and access boundaries. It exposes a variety of frontends (emulating serial ports, ModBus TCP over UDS or IP with and without (m)TLS support) as well as backends (serial ports, ModBus IP targets, etc.). It allows multiple applications to access the same backends, providing arbitration and synchronization.
This project has been developed to allow multiple services to access various services attached to the same hardware ModBus network on a machine exploiting multiple RS485 interfaces (identified via unique device names).
Features
- Pluggable architecture: Modular frontends and backends, allowing further extensions in future versions.
- Multiple frontends: Virtual serial ports (
pty), ModBus TCP over TCP and Unix domain sockets - Secure TCP support: TLS and mTLS (with client certificate authentication) for ModBus TCP over TCP/IP
- Multiple backends: Hardware serial ports and ModBus TCP IP backends
- Flexible Routing Engine: Map device IDs and registers between frontends and backends; split or aggregate devices across multiple backends and passthrough mode for transparent forwarding
- Security Filtering: Per-frontend filtering rules enforcing access boundaries on register/device level. Ideal for isolating subsystems or exposing limited views.
- Future extensions: Planned MQTT interface for IoT integration
Work in Progress
The following features are still work in progress:
- MQTT support
- REST API support
Installation
The gateway can be installed via PyPi via
pip install modbus-gateway
or from the repository root via
pip install -e .
The associated client library is available via
pip install modbusgw-client
Again it can also be installed from the repositories modbusgw_client
subdirectory via
pip install -e ./modbusgw_client/
Configuration
The default configuration file is located at ~/.config/modbusgateway.cfg. It
is composed of a single large JSON dictionary consisting of the following keys:
serviceprovides configuration of the main daemonbusconfigures the internal message busfrontendscontains a list of frontend configurations over which clients are capable of accessing the daemonbackendsis the counterparts and defines the interfaces that are accessed on behalf of the clients via the gateway.routesprovides a match-list based configuration on how to route messages between frontends and backends.
The service section configures PID file to prevent multiple running
instances, the state directory that will be used for log- and tracefiles
as well as the loglevel:
"service" : {
"log_level" : "INFO",
"pid_file" : "/var/run/modbusgw.pid",
"state_dir" : "/var/modbusgw/",
"reload_grace_seconds" : 5
}
The bus configuration configures the internal buffer for incoming requests
that are routed to various backends:
"bus" : {
"request_queue_size" : 64,
"response_timeout_ms" : 1500
}
Note that this timeout should be shorter than the applications and frontends timeouts.
Frontend Configurations
Virtual Serial Ports (pty)
Virtual serial ports are directly accessible via pyserial and similar interfaces.
This allows existing legacy software to access the gateway via unmodified code by
pointing it at the virtual serial port file handles:
{
"id" : "virtual_serial_rtu",
"type" : "serial_rtu_socket",
"socket_path" : "/var/modbusgw/ttyBus0",
"pty_mode" : "rw",
"idle_close_seconds" : 600,
"frame_timeout_ms" : 5.0
}
The shown configuration instantiates a virtual serial port at the specified socket_path,
allowing read-write transactions. The frame timeout handles incomplete messages on the
application side. The name virtual_serial_rtu is an arbitrary chosen name that is
used in the routing configuration.
ModBus IP TCP Socket
A ModBus IP socket speaks the ModBus IP protocol over an TCP socket (optionally supporting TLS or mTLS for authenticated sessions). The following configuration exposes unencrypted ModBus IP applying only IP subnet based filters:
{
"id" : "frontend_tcp",
"type" : "tcp_modbus_tcp",
"host" : "192.0.2.1",
"port" : 1234,
"cidr_allow" : [
"127.0.0.0/8",
"192.0.2.0/24"
]
}
If TLS is desired the following configuration can be added to the frontend configuration object:
"tls" : {
"cert_file" : "/path/to/server.crt",
"key_file" : "/path/to/server.key",
"ca_file" : "/path/to/rootca.crt",
"require_client_cert" : true,
"client_dn_allow" : [
"CN=ModbusGW Test Client"
]
}
The cert_file and key_file establish the server identity. The ca_file is only
used when require_client_cert is set to true to allow client authentication. The
additional (optional) client_dn_allow filter allows to filter the DNs from
valid certificates (after certificate validation) that are allowed to access the frontend.
Backend Configurations
Hardware Serial Ports
The pyserial backend uses the pyserial library
to access an USB to RS485 based interface. This is the most simple hardware interface
for DIY setups. The specified serial configuration is applied when accessing the backend.
Again the arbitrary id is used in the routing configuration.
{
"id" : "hardware_serial",
"type" : "pyserial",
"device" : "/dev/ttyU0",
"baudrate" : 9600,
"parity" : "N",
"stop_bits" : 1,
"request_timeout_ms" : 1200
}
ModBus IP via TCP
A TCP backend can be configured via the tcp_modbus backend:
{
"id" : "tcp_backend",
"type" : "tcp_modbus",
"host" : "127.0.0.1",
"port" : 1234,
"connect_timeout" : 2.0,
"pool_size" : 2,
"use_tls" : true,
"tls" : {
"ca_file" : "/path/to/root.crt",
"cert_file" : "/path/to/client.crt",
"key_file" : "/path/to/client.key"
}
}
The use_tls and tls blocks are optional and are only used when (m)TLS is
desired. The root.crt is used for validation, the client keys for authentication
via mTLS.
Routing Configuration
The routing configuration is provided as a list of routing commands that are matched
against incoming requests from the frontends. The first match determines to which backend
a message is routed. The backend key and the mirror_to_mqtt key is not used
for matching, all other fields apply:
{
"frontend" : "virtual_serial_rtu",
"backend" : "hardware_serial",
"match" : {
"unit_ids" : [ "*" ],
"function_codes" : [ "*" ]
},
"mirror_to_mqtt" : [ ]
}
The routing match block allows to filter given device IDs and function codes
as well as operations. For example to allow only function code 1 (read coils)
for the virtual device 5, redirecting the operation to the backend device id 1,
one would use
{
"frontend" : "virtual_serial_rtu",
"backend" : "hardware_serial",
"match" : {
"unit_ids" : [ 5 ],
"function_codes" : [ 1 ],
"operations" : [ "read" ]
},
"unit_override" : 1,
"mirror_to_mqtt" : [ ]
}
Here the match block specifies conditions that have to be fulfilled (all
have to be fulfilled). The optional unit_override replaces the device ID
on the virtual frontend bus to the given unit number before handing off the
the backend device. All fields can be used in arbitrary combinations.
Example configuration file
The following configuration exposes a single serial to RS485 interface via a local virtual serial port as well as a ModBus IP socket available via unencrypted TCP:
{
"service" : {
"log_level" : "INFO",
"pid_file" : "/var/run/modbusgw.pid",
"state_dir" : "/var/modbusgw/",
"reload_grace_seconds" : 5
},
"bus" : {
"request_queue_size" : 64,
"response_timeout_ms" : 1500
},
"frontends" : [
{
"id" : "virtual_serial_rtu",
"type" : "serial_rtu_socket",
"socket_path" : "/var/modbusgw/ttyBus0",
"pty_mode" : "rw",
"idle_close_seconds" : 600,
"frame_timeout_ms" : 5.0
},
{
"id" : "frontend_tcp",
"type" : "tcp_modbus_tcp",
"host" : "192.0.2.1",
"port" : 1234,
"cidr_allow" : [
"127.0.0.0/8",
"192.0.2.0/24"
]
}
],
"backends" : [
{
"id" : "hardware_serial",
"type" : "pyserial",
"device" : "/dev/ttyU0",
"baudrate" : 9600,
"parity" : "N",
"stop_bits" : 1,
"request_timeout_ms" : 1200
}
],
"routes" : [
{
"frontend" : "virtual_serial_rtu",
"backend" : "hardware_serial",
"match" : {
"unit_ids" : [ "*" ],
"function_codes" : [ "*" ]
},
"mirror_to_mqtt" : [ ]
},
{
"frontend" : "frontend_tcp",
"backend" : "hardware_serial",
"match" : {
"unit_ids" : [ "*" ],
"function_codes" : [ "*" ]
},
"mirror_to_mqtt" : [ ]
}
]
}
FreeBSD rc.init script
The repository contains an rc.init script in the rc.d subdirectory. This is suited
for FreeBSDs rc.init infrastructure and allows setting startup parameters in rc.conf:
modbusgw_enable="YES"
modbusgw_user="modbusgw"
modbusgw_group="modbusgw"
The default configuration file location when using the rc.init script is /usr/local/etc/modbusgw/modbusgateway.cfg,
which can be overriden via the modbusgw_config variable. In addition the script ensures
the presence and writeability of the /var/modbusgw directory. The script supports:
startstopstatusrestartreload(note that this does not reload the configuration for logging!)
Running
When not using the rc.init script mentioned above one can launch the application in
foreground via
$ modbusgw
To execute the daemon simply add the appropriate lifecycle commands:
$ modbusgw start
$ modbusgw stop
$ modbusgw status
$ modbusgw restart
$ modbusgw reload
Client Library
This repository also contains an independent client library for interacting with ModBus systems via serial ports or ModBus TCP. The documentation is found in the modbusgw-client directory.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file modbus_gateway-0.0.3.tar.gz.
File metadata
- Download URL: modbus_gateway-0.0.3.tar.gz
- Upload date:
- Size: 33.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
667545460562ee86b65905ac3cdbb15713e3c5538684301592e7dc4e6b10f4d3
|
|
| MD5 |
348f42b8120ec95847a75da6655670f4
|
|
| BLAKE2b-256 |
004a8c1a1d5a28a8ea47dfab09f82906f9910f42c30e698e538515e9c912b8c3
|
File details
Details for the file modbus_gateway-0.0.3-py3-none-any.whl.
File metadata
- Download URL: modbus_gateway-0.0.3-py3-none-any.whl
- Upload date:
- Size: 35.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4c4050c3401db3198428d047da6305012754242a92c4e69d7bbfd6209f0346f
|
|
| MD5 |
2a4fbf5c7a21e0f339aa3ac816ca0902
|
|
| BLAKE2b-256 |
9091b3b33077275417c1d4aeb0982706910c25d6484e4dd535b30ce81044375b
|