A tokio-based modbus library

Overview

tokio-modbus

A tokio-based modbus library.

Crates.io version Docs Build Status Build status Coverage Status

Features

  • pure Rust library
  • async (non-blocking)
  • sync (blocking)
  • Modbus TCP
  • Modbus RTU
  • Client & Server
  • Open Source (MIT/Apache-2.0)

Installation

Add this to your Cargo.toml:

[dependencies]
tokio-modbus = "*"

If you like to use Modbus TCP only:

[dependencies]
tokio-modbus = { version = "*", default-features = false, features = ["tcp"] }

If you like to use Modbus RTU only:

[dependencies]
tokio-modbus = { version = "*", default-features = false, features = ["rtu"] }

If you like to build a TCP server:

[dependencies]
tokio-modbus = { version = "*", default-features = false, features = ["tcp", "server"] }

Examples

Various examples for Modbus RTU and TCP using either the asynchronous or synchronous API can be found in the examples folder.

Protocol-Specification

License

Copyright 2017 - 2020 slowtec GmbH

MIT/Apache-2.0

Comments
  • Windows support

    Windows support

    main.rs

    extern crate futures;
    extern crate tokio_core;
    extern crate tokio_modbus;
    extern crate tokio_serial;
    
    use tokio_core::reactor::Core;
    use futures::future::Future;
    use tokio_serial::{Serial, SerialPortSettings};
    use tokio_modbus::*;
    
    pub fn main() {
        let mut core = Core::new().unwrap();
        let handle = core.handle();
        let port_path = "COM1";
        let server_addr = 0x01;
    
        let mut settings = SerialPortSettings::default();
        settings.baud_rate = 19200;
        let mut port = Serial::from_path(port_path, &settings, &handle).unwrap();
        port.set_exclusive(false).unwrap();
    
        let task = Client::connect_rtu(port, server_addr, &handle).and_then(|client| {
            println!("Reading a sensor value");
            client
                .read_holding_registers(0x082B, 2)
                .and_then(move |res| {
                    println!("Sensor value is: {:?}", res);
                    Ok(())
                })
        });
    
        core.run(task).unwrap();
    }
    

    Cargo.toml

    [dependencies]  
    futures = "*"  
    tokio = "*"  
    tokio-core = "*"
    tokio-serial = "*"
    tokio-service = "*"
    tokio-modbus = { version = "*", default-features = false, features = ["rtu"] }
    

    Results

    C:/Users/test/.cargo/bin/cargo.exe run --package modbus_test --bin modbus_test
       Compiling tokio-modbus v0.2.1
    error[E0432]: unresolved import `tokio_serial::Serial`
     --> C:\Users\test\.cargo\registry\src\github.com-1ecc6299db9ec823\tokio-modbus-0.2.1\src\service\rtu.rs:3:5
      |
    3 | use tokio_serial::Serial;
      |     ^^^^^^^^^^^^^^^^^^^^ no `Serial` in the root
    
    error[E0432]: unresolved imports `tokio_serial::Serial`, `tokio_serial::SerialPortSettings`
     --> C:\Users\test\.cargo\registry\src\github.com-1ecc6299db9ec823\tokio-modbus-0.2.1\src\client.rs:9:20
      |
    9 | use tokio_serial::{Serial, SerialPortSettings};
      |                    ^^^^^^  ^^^^^^^^^^^^^^^^^^ no `SerialPortSettings` in the root
      |                    |
      |                    no `Serial` in the root
    
    error[E0433]: failed to resolve. Could not find `tcp` in `proto`
      --> C:\Users\test\.cargo\registry\src\github.com-1ecc6299db9ec823\tokio-modbus-0.2.1\src\server.rs:75:39
       |
    75 |                 TcpServer::new(proto::tcp::Proto, addr)
       |                                       ^^^ Could not find `tcp` in `proto`
    
    error[E0277]: the size for values of type `dyn futures::Future<Item=frame::Response, Error=std::io::Error>` cannot be known at compilation time
      --> C:\Users\test\.cargo\registry\src\github.com-1ecc6299db9ec823\tokio-modbus-0.2.1\src\service\rtu.rs:52:9
       |
    52 |         Box::new(result)
       |         ^^^^^^^^ doesn't have a size known at compile-time
       |
       = help: the trait `std::marker::Sized` is not implemented for `dyn futures::Future<Item=frame::Response, Error=std::io::Error>`
       = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
       = note: required by `<std::boxed::Box<T>>::new`
    
    error[E0277]: the size for values of type `dyn futures::Future<Item=frame::Response, Error=std::io::Error>` cannot be known at compilation time
      --> C:\Users\test\.cargo\registry\src\github.com-1ecc6299db9ec823\tokio-modbus-0.2.1\src\service\rtu.rs:40:13
       |
    40 |         let result = self.service.call(req).and_then(move |resp| {
       |             ^^^^^^ doesn't have a size known at compile-time
       |
       = help: the trait `std::marker::Sized` is not implemented for `dyn futures::Future<Item=frame::Response, Error=std::io::Error>`
       = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
       = note: all local variables must have a statically known size
       = help: unsized locals are gated as an unstable feature
    
    error: aborting due to 5 previous errors
    
    Some errors occurred: E0277, E0432, E0433.
    For more information about an error, try `rustc --explain E0277`.
    error: Could not compile `tokio-modbus`.
    
    To learn more, run the command again with --verbose.
    
    Process finished with exit code 101
    
    
    enhancement 
    opened by aleksey-r 18
  • Moving to async await

    Moving to async await

    We have been using this library a lot here at omnio and are working on updating our code base to use the new async features. We have already helped update tokio-serial here and would love to see tokio-modbus updated as well. We internally built an updated version of tokio-modbus, and you can find the changes in this PR.

    This pull requests still needs some polishing, but we would like to hear your opinion on the direction we've been taking the library before continuing the implementation, especially regarding the removal of tokio-proto, which has a certain impact on the external api:

    1. Several methods now require &mut self instead of &self.
    2. These methods also borrow self inside the returned future.

    The tokio-proto crate previously supplied a layer that uses interior mutability to put requests in a queue and later dispatch the results to the appropriate future. As proto is now gone, this dispatching mechanism is also gone, which means that we instead have to rely on mutable borrows to ensure at compile time that at most a single modbus request exists per connection at any time.

    The disadvantage of modifying the api in this way is that it can be harder to use the api: previously you were able to just create a bunch of requests and await them all. Now you must create the next request when the preceding one finishes.

    Luckily it also turns out that the introduction of async makes the kind of loop described above much easier to write: The introduction of pin makes futures that borrow from the modbus context usable, and the ability to write a loop in async code avoids certain issues with using for_each to create the requests.

    // example of using a loop to read many requests.
    let requests_i_want = vec![...];
    let results = Vec::with_capacity(requests_i_want.len());
    for request in requests_i_want {
        let result = modbus_context.call(request).await?;
        results.push(result);
    }
    return results;
    

    There are also quite a few traits that return a Pin<Box<...>>. It would be nice to use associated types in the traits to return non-heap-allocated futures instead, but we have not looked into implementing that yet.

    enhancement 
    opened by Darksonn 13
  • Add libudev feature to enable/disable tokio-serial/libudev

    Add libudev feature to enable/disable tokio-serial/libudev

    When trying to cross-compile this crate for a Raspberry Pi (armv7-unknown-linux-gnueabihf), the libudev dependency of tokio-serial failed to build.

    I considered two ways of addressing this: Either set up proper cross-compilation for libudev, or remove it as a dependency. I tried the latter, and it appears that libudev is not needed as a dependency in this project.

    opened by laumann 13
  • Read/write timeout

    Read/write timeout

    When you run the example in Windows 10 and there is no response to reading the registers from the device, the program just hangs without returning an error. Is it possible to set a read/write timeout?

    bug enhancement 
    opened by aleksey-r 13
  • New client/server API

    New client/server API

    Some technical changes for the next evolution of the API:

    • New prelude module that includes all public exports
    • Isolate features in submodules
      • rtu / tcp
      • sync
    • Split ModbusClient trait into Client + Reader + Writer traits
    • Rename Client into Connection
      • synchronous / asynchronous
    • Provide various connect() functions in submodules for establishing client connections
      • TCP / RTU
      • synchronous / asynchronous
    • Move TCP server into submodule
    • Client: Switch between different slaves dynamically at runtime

    Migration instructions can be found in CHANGELOG.md and the examples reflect what users of this crate might need to change.

    Maybe we should declare the server-side as experimental? Currently users are not able to control the TCP server instance once it has been attached to a port. This will hopefully change when we replace tokio-core and tokio-proto.

    opened by uklotzde 13
  • Cannot run RTU client sync example

    Cannot run RTU client sync example

    • I wanted to give to give the Modbus RTU client examples a test drive but failed in both cases
      • rtu-client-sync.rs panics with
        $ cargo run --features rtu,sync --example rtu-client-sync
           Compiling tokio-modbus v0.5.2 (/.../tokio-modbus)
            Finished dev [unoptimized + debuginfo] target(s) in 0.85s
             Running `target/debug/examples/rtu-client-sync`
        thread 'main' panicked at 'there is no reactor running, must be called from the context of a Tokio 1.x runtime', /.../.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.14.0/src/runtime/context.rs:29:26
        note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
        
    • Is this examples expected to run out of the box?
    • What could I do to actually get it running?
    • At a first first glance into context.rs:29 it looks like borrowing from CONTEXT fails and I bet there is some initialization missing. But this is my first contact with Tokio and I have no idea about the what, how, and why.
    opened by sirhcel 12
  • RTU server

    RTU server

    Adds RTU server functionality based on #72

    This PR simply replicates that previous proposal that was sidelined by the #78 merge, but updated to compile based on the changes in tokio-serial.

    Functionality has absolutely not been field-tested by me (beyond the provided example code), and since I have no practical use-case for an RTU server application, I'd ask someone more involved in this field (like @efancier-cn @ivomurrell or @david-mcgillicuddy-moixa) to do the necessary validations.

    Closes #73

    opened by bootrecords 11
  • Add documentation to the Request type

    Add documentation to the Request type

    The type aliases that are defined in the code are not visible in the documentation, so it's better to have an explicit documentation.

    Here is what the current documentation looks like on docs.rs: image https://docs.rs/tokio-modbus/0.3.5/tokio_modbus/prelude/enum.Request.html

    The type aliases such as Address, Coil, Quantity, etc. are not visible in the documentation.

    enhancement 
    opened by lovasoa 9
  • tcp-server.rs tag v0.4.0 doesn't compile

    tcp-server.rs tag v0.4.0 doesn't compile

    Hello,

    Last week (11th June) I have been testing 'tcp-server.rs' from 'slowtec/tokio-modbus/examples' and there is some kind of issue related to 'tcp' and 'server' features. If you try to compile 'tcp-server.rs' you will receive an error and I think that it comes from the feature 'server'. It is like it cannot be found even writing it in 'dependencies' in 'Cargo.toml'

    However, I tried 'tcp-server.rs' tag v0.3.5 and it runs without problem.

    opened by jpinafcirce 8
  • Upgrade to tokio 1.0.

    Upgrade to tokio 1.0.

    Now that tokio 1.0 is finally out, it seems like a good time to upgrade to the stable version so that other projects can use tokio-modbus after upgrading to tokio 1.0. Currently using the tokio 1.0 update for tokio-serial that is still in PR form so will have to treat this as a draft until that is merged and published. Nevertheless, seems like a fairly straightforward migration!

    Closes #71

    enhancement discussion 
    opened by ivomurrell 8
  • Unable to Close TCP Client Connection

    Unable to Close TCP Client Connection

    Target: ARMv7 (musl)

    I have a warp webserver that upon a request will open a connection and read some registers, and I want the connection to close after the registers are successfully read. Using the async API, I expected the TCP client connections to close when contexts are dropped, but they do not (netstat shows them in established state). I tried Context::disconnect, but that causes a panic ('internal error: entered unreachable code'). What is the correct way to disconnect the TCP connection?

    let read_input_registers = get2()
            .and(path!("rir" / u16 / u16)
            .and(path::end())
            .and_then(move |start, count|{
                let (p, c) = futures::sync::oneshot::channel::<String>();
                remote.spawn(move |handle|
                    client::tcp::connect(handle, localhost)
                    .and_then(move |ctx| ctx.read_input_registers(start, count)/*.and_then(move |res| ctx.disconnect().then(|_| Ok(res)))*/)
                    .then(|res|{
                        p.send(match res{
                            Ok(data) => format!("{:?}", data),
                            Err(err) =>  format!("{:?}", err)
                        }).unwrap_or(());
                        Ok(())
                    })
                );
                c.map_err(|err| warp::reject::custom(err))
            }));
    

    I've switched to the modbus crate until I can figure this out. I'd prefer to use this one since it's non-blocking.

    bug help wanted 
    opened by DBTaylor 8
  • Fetching the coupler ID

    Fetching the coupler ID

    Only use tokio and tokio-modbus, and run the tcp-client example code, but show Fetching the coupler ID

    Reproduction:

    1. cargo new a new project
    2. add tokio-modbus = "0.5.3", tokio = {version = "=1.22.0", features = ["full"]} to Cargo.toml
    3. copy the example tcp-client code to main.rs
    4. run cargo build, success, but cargo run show:
    D:\xxx\yyy>cargo run
        Finished dev [unoptimized + debuginfo] target(s) in 0.11s
         Running `target\debug\tokio-modbus.exe`
    Fetching the coupler ID
    

    Environment: Win10 rustc 1.65.0 (897e37553 2022-11-02) rustup 1.25.1 (bb60b1e89 2022-07-12) x86_64-pc-windows-msvc Modbus server use the Qt QModubs example's modbusslave.exe, with the configuration: tcp, 127.0.0.1:502 (modbusmaster.exe can work well)

    opened by thy1037 0
  • expected enum `winapi::ctypes::c_void`, found enum `std::ffi::c_void`

    expected enum `winapi::ctypes::c_void`, found enum `std::ffi::c_void`

    When I add tokio-modbus as a dependencies, cargo build will failed.

    Reproduction:

    1. cargo new a new project
    2. add tokio-modbus = "0.5.3" to Cargo.toml
    3. copy the example tcp-client code to main.rs
    4. run cargo build, and show:
       Compiling mio-serial v5.0.3
    error[E0308]: mismatched types
       --> C:\Users\dell\.cargo\registry\src\mirrors.ustc.edu.cn-61ef6e0cd06fb9b8\mio-serial-5.0.3\src\lib.rs:822:42
        |
    822 |         let r = unsafe { SetCommTimeouts(handle, &mut timeouts) };
        |                          --------------- ^^^^^^ expected enum `winapi::ctypes::c_void`, found enum `std::ffi::c_void`
        |                          |
        |                          arguments to this function are incorrect
        |
        = note: expected raw pointer `*mut winapi::ctypes::c_void`
                   found raw pointer `*mut std::ffi::c_void`
    note: function defined here
       --> C:\Users\dell\.cargo\registry\src\mirrors.ustc.edu.cn-61ef6e0cd06fb9b8\winapi-0.3.9\src\um\commapi.rs:74:12
        |
    74  |     pub fn SetCommTimeouts(
        |            ^^^^^^^^^^^^^^^
    
    For more information about this error, try `rustc --explain E0308`.
    error: could not compile `mio-serial` due to previous error
    

    Environment: Win10 rustc 1.65.0 (897e37553 2022-11-02) rustup 1.25.1 (bb60b1e89 2022-07-12) x86_64-pc-windows-msvc

    opened by thy1037 3
  • Fix/tcp delay causing composite tcp packets

    Fix/tcp delay causing composite tcp packets

    I was investigating a problem and noticed that we could trigger the modbus lib to 'collect' multiple Modbus ADUs into a single TCP PDU.

    This is from Wireshark showing the trace between the client (a nodejs script for testing purposes) and the server (baked with tokio-modbus): image

    Notice that packet 113 has multiple responses, which can be viewed: image

    I am pretty sure that this behavior is causing a problem for one of our customers, who is using a PLC which does support a list of Modbus ADUs in a single TCP PDU. In the other cases our customers PLCs must be waiting for the reponse before making a new request.

    From the Modbus_Messaging_Implementation_Guide_V1_0b.pdf in your README, it seems that this should not be done at all. At page 10: image

    This fix is simply to disable Nagle's algorithm by setting nodelay = true. This can not be done the tokio::net::TcpListener directly so I was forced to use from_std and constructing a standard socket.

    I was considering making the nodelay a setting, but as it advised not to use it in the document above, I think it should be standard.

    This is what it looks like after the fix. Note that packet 102 / Transaction 5 has 3 requests (Modbus ADUs) in it. The Decoder is perfectly cable of handling this response . image

    tcp 
    opened by thosaa 4
  • MODBUS/TCP Security support

    MODBUS/TCP Security support

    Hi. Congrats for the project. Is it possible to add support for the new specification, the Modbus TCP Security, based on TLS?

    Some of new protocols based on modbus are migrating for this modbus version, as the sunspec modbus protocol. The specification is available at: https://modbus.org/docs/MB-TCP-Security-v21_2018-07-24.pdf

    I beleive it can be implemented based on tokio-rustls.

    Thanks in advance. Gustavo Denardin

    tcp 
    opened by gustavowd 1
  • Floating point support

    Floating point support

    Hi, is it possible to pass a f32 float with quantity 2 via two u16 registers and only get 2 buffer values? When I pass 2 u16's into the input register vector, it returns 4 buffer values in my modbus client.

    question 
    opened by cblackd7r 0
Releases(v0.5.1)
  • v0.4.1(Aug 13, 2021)

  • v0.4.0(Jul 28, 2021)

    • New public API: moved to async/await and tokio v0.2.x
    • Removed unmaintained dependency tokio-proto
    • Make Exception and ExceptionResponse public
    • Fixed WriteSingleCoil response to include data
    • Hide server traits Service/NewService traits behind server feature
    • Hide TCP server implementation behind tcp-server-unstable feature
    • Improved documentation
    Source code(tar.gz)
    Source code(zip)
  • v0.3.5(Sep 17, 2019)

  • v0.3.4(May 21, 2019)

  • v0.3.3(May 16, 2019)

  • v0.3.2(Apr 15, 2019)

    • Client: Added a Disconnect request as poison pill for stopping the client service and to release the underlying transport
    • Added utilities to share a single Modbus context within a thread for communicating with multiple devices
    • Added utility functions to disconnect and reconnect stale connections after errors
    • Minimal Rust version: 1.34.0

    Potential breaking change

    Extending the Request enum with the new variant Disconnect might break existing code. This variant is only used internally within the client and will never be sent across the wire and can safely be ignored by both clients and servers!

    Source code(tar.gz)
    Source code(zip)
  • v0.3.1(Apr 8, 2019)

  • v0.3.0(Apr 4, 2019)

    • New public API
    • Client: Change devices while connected
    • TCP Client: Connect to RTU devices via gateway (unit identifier)
    • RTU Client: Try to recover from frame errors
    Source code(tar.gz)
    Source code(zip)
Owner
slowtec GmbH
Sustainable Technology - S(mart)lowtec Engineering
slowtec GmbH
The Modbus IIoT Library

The Modbus IIoT Library A pure Rust library for working with Modbus in IoT/IIoT projects. It is based on the Modbus protocol specification with the fo

Bianco Royal Space 13 Mar 14, 2022
Many modbus devices support only one or very few clients

Modbus TCP proxy Many modbus devices support only one or very few clients. This proxy acts as a bridge between the client and the modbus device. It ca

Tiago Coutinho 6 Aug 10, 2022
Utilities for tokio/tokio-uring based async IO

dbs-fuse The dbs-fuse is a utility crate to support fuse-backend-rs. Wrappers for Rust async io It's challenging to support Rust async io, and it's ev

OpenAnolis Community 6 Oct 23, 2022
Simple crate that wraps a tokio::process into a tokio::stream

tokio-process-stream tokio-process-stream is a simple crate that wraps a tokio::process into a tokio::stream Having a stream interface to processes is

Leandro Lisboa Penz 8 Sep 13, 2022
Tokio based client library for the Android Debug Bridge (adb) based on mozdevice

forensic-adb Tokio based client library for the Android Debug Bridge (adb) based on mozdevice for Rust. Documentation This code has been extracted fro

null 6 Mar 31, 2023
Asyncronous Rust Mysql driver based on Tokio.

mysql-async Tokio based asynchronous MySql client library for rust programming language. Installation Library hosted on crates.io. [dependencies] mysq

Anatoly I 292 Dec 30, 2022
Thread Safe Cache with async loader functions based on tokio-rs

cache-loader-async crates.io The goal of this crate is to provide a thread-safe and easy way to access any data structure which might is stored in a d

ZeroTwo Bot 13 Nov 30, 2022
Fork of async-raft, the Tokio-based Rust implementation of the Raft protocol.

Agreed Fork of async-raft, the Tokio-based Rust implementation of the Raft distributed consensus protocol. Agreed is an implementation of the Raft con

NLV8 Technologies 8 Jul 5, 2022
Provides utility functions to perform a graceful shutdown on an tokio-rs based service

tokio-graceful-shutdown IMPORTANT: This crate is in an early stage and not ready for production. This crate provides utility functions to perform a gr

null 61 Jan 8, 2023
Services Info Register/KeepAlive/Publish/Subscribe. Based on etcd-rs, tokio

Services Info Register/KeepAlive/Publish/Subscribe. Based on etcd-rs, tokio

Mutalisk 5 Oct 4, 2022
Pure Rust library for Apache ZooKeeper built on tokio

zookeeper-async Async Zookeeper client written 100% in Rust, based on tokio. This library is intended to be equivalent with the official (low-level) Z

Kamil Rojewski 16 Dec 16, 2022
An actors library for Rust and Tokio designed to work with async / await message handlers out of the box.

Akt An actors framework for Rust and Tokio. It is heavily inspired by Actix and right now it has very similar look and feel. The main difference is th

Artyom Kozhemiakin 7 Jan 10, 2023
a tokio-enabled data store for triple data

terminusdb-store, a tokio-enabled data store for triple data Overview This library implements a way to store triple data - data that consists of a sub

TerminusDB 307 Dec 18, 2022
Performs distributed command execution, written in Rust w/ Tokio

Concurr: Distributed and Concurrent Command Execution, in Rust This project is dual licensed under MIT and Apache 2.0. Originally inspired by the GNU

Michael Murphy 93 Dec 18, 2022
Asynchronous Linux SocketCAN sockets with tokio

tokio-socketcan SocketCAN support for tokio based on the socketcan crate. Example echo server use futures_util::stream::StreamExt; use tokio_socketcan

Terry 29 Nov 8, 2022
Mix async code with CPU-heavy thread pools using Tokio + Rayon

tokio-rayon Mix async code with CPU-heavy thread pools using Tokio + Rayon Resources Documentation crates.io TL;DR Sometimes, you're doing async stuff

Andy Barron 74 Jan 2, 2023
A tokio-uring backed runtime for Rust

tokio-uring A proof-of-concept runtime backed by io-uring while maintaining compatibility with the Tokio ecosystem. This is a proof of concept and not

Tokio 726 Jan 4, 2023
Rust Tokio 异步多客户端网络框架 高并发 插件化

Rust实现的异步多客户端网络框架,基于tokio和mlua,可自定义通讯协议 插件化采用lua。应用场景im,game server,bot等.

OPQBOT 22 Jul 19, 2022
tokio-console prototypes

tokio-console prototypes ⚠️ extremely serious warning: this is pre-alpha, proof-of-concept software! currently, the wire format has no stability guara

Tokio 2.3k Jan 3, 2023
A super minimal wrapper around unix sockets for IPC on top of tokio.

tokio-unix-ipc This crate implements a minimal abstraction over UNIX domain sockets for the purpose of IPC on top of tokio.

Armin Ronacher 26 Nov 18, 2022