A framework to build fast and reliable Modbus-powered applications.
What is rmodbus
rmodbus is not a yet another Modbus client/server. rmodbus is a set of tools to quickly build Modbus-powered applications. Consider rmodbus is a request/response codec, plus context manager.
rmodbus is a part of EVA ICS v4 industrial automation stack and RoboPLC I/O.
Why yet another Modbus lib?
- rmodbus is transport- and protocol-independent
- rmodbus is platform independent (
no_stdis fully supported!) - can be easily used in blocking and async (non-blocking) applications
- tuned for speed and reliability
- provides a set of tools to easily work with Modbus context
- supports client/server frame processing for Modbus TCP/UDP, RTU and ASCII
- server context can be easily managed, imported and exported
So no server is included?
Yes, there is no server included. You build the server by your own. You choose the transport protocol, technology and everything else. rmodbus just process frames and works with Modbus context.
For synchronous servers and clients (std) we recommend using RoboPLC Modbus I/O modules.
Here is an example of a simple TCP blocking server:
use ;
use TcpListener;
use thread;
use RwLock;
use Lazy;
use ;
static CONTEXT: = new;
There are also examples for Serial-RTU, Serial-ASCII and UDP in examples folder (if you're reading this text somewhere else, visit rmodbus project repository.
Launch the examples as:
cargo run --example app
cargo run --example tcpserver
Modbus context
The rule is simple: one standard Modbus context per application. 10k+10k 16-bit registers and 10k+10k coils are usually more than enough. This takes about 59Kbytes of RAM.
rmodbus server context is thread-safe, easy to use and has a lot of functions.
The context must be protected with a mutex/rwlock and every time Modbus context is accessed, a context mutex must be locked. This slows down performance, but guarantees that the context always has valid data after bulk-sets and after writes of long data types. So make sure your application locks context only when required and only for a short period time.
A simple PLC example:
use Error;
use File;
use ;
use ;
// put 1 to holding register 1500 to save current context to /tmp/plc1.dat
// if the file exists, context will be loaded at the next start
To let the above program communicate with outer world, Modbus server must be up and running in the separate thread, asynchronously or whatever is preferred.
no_std
rmodbus supports no_std mode. Most of the library code is written the way to
support both std and no_std.
For no_std, set the dependency as:
= { = "*", = false }
Small storage
The full Modbus storage has 10000 registers of each type, which requires 60000 bytes total. For systems with small RAM amount there is a pre-defined small storage with 1000 registers:
use ;
Custom-sized storage
Starting from the version 0.7 it is allowed to define storage of any size using generic constants. The generic constants order is: coils, discretes, inputs, holdings.
E.g. let us define a context for 128 coils, 16 discretes, 0 inputs and 100 holdings:
use ;
let context = new;
Custom server implementation
Starting from the version 0.9 it is allowed to provide custom server implementation
by implementing use rmodbus::server::context::ModbusContext on custom struct.
For sample implementation have a look at src/server/storage.rs
Custom type representations in u16 sized registers
Starting from version <todo: insert version number here>, you can implement
server::RegisterRepresentable<N> on your own types and use
ModbusContext::set_*_as_representable and ModbusContext::get_*_as_representable
methods to directly store and read your own types in the registers.
Vectors
Some of rmodbus functions use vectors to store result. Different vector types can be used:
-
When the
stdfeature is enabled (default),std::vec::Veccan be used. -
With the
fixedvecfeature,fixedvec::FixedVeccan be used. -
With the
heaplessfeature,heapless::Veccan be used. -
When the
allocfeature is enabled, Rust core allocationalloc::vec::Veccan be used in no-std mode. E.gcargo build --no-default-features --features allocbuilds in no-std mode, and supports using core allocationalloc::vec::Vec. Whenstdfeature is enabled, theallocfeature is ignored.
Modbus client
Modbus client is designed with the same principles as the server: the crate gives frame generator / processor, while the frames can be read / written with any source and with any required way.
TCP client Example:
use ;
use TcpStream;
use Duration;
use ;
let timeout = from_secs;
// open TCP connection
let mut stream = connect.unwrap;
stream.set_read_timeout.unwrap;
stream.set_write_timeout.unwrap;
// create request object
let mut mreq = new;
mreq.tr_id = 2; // just for test, default tr_id is 1
// set 2 coils
let mut request = Vecnew;
mreq.generate_set_coils_bulk
.unwrap;
// write request to stream
stream.write.unwrap;
// read first 6 bytes of response frame
let mut buf = ;
stream.read_exact.unwrap;
let mut response = Vecnew;
response.extend_from_slice;
let len = guess_response_frame_len.unwrap;
// read rest of response frame
if len > 6
// check if frame has no Modbus error inside
mreq.parse_ok.unwrap;
// get coil values back
mreq.generate_get_coils.unwrap;
stream.write.unwrap;
let mut buf = ;
stream.read_exact.unwrap;
let mut response = Vecnew;
response.extend_from_slice;
let len = guess_response_frame_len.unwrap;
if len > 6
let mut data = Vecnew;
// check if frame has no Modbus error inside and parse response bools into data vec
mreq.parse_bool.unwrap;
for i in 0..data.len
About the authors
Bohemia Automation / Altertech is a group of companies with 15+ years of experience in the enterprise automation and industrial IoT. Our setups include power plants, factories and urban infrastructure. Largest of them have 1M+ sensors and controlled devices and the bar raises higher and higher every day.