[][src]Crate rodbus

A high-performance implementation of the Modbus protocol using Tokio and Rust's async/await syntax.

Features

  • Panic-free parsing
  • Focus on maximal correctness and compliance to the specification
  • Automatic connection management with configurable reconnect strategy
  • Scalable performance using Tokio's multi-threaded executor
  • async (futures), callbacks, and synchronous API modes
  • Idiomatic C API for integration with legacy codebases

Supported modes

  • TCP client and server

Supported Functions

  • Read Coils
  • Read Discrete Inputs
  • Read Holding Registers
  • Read Input Registers
  • Write Single Coil
  • Write Single Register
  • Write Multiple Coils
  • Write Multiple Registers

Future support

  • TLS Client / TLS Server + Modbus X.509 extensions using Rustls
  • Additional function code support
  • Modbus RTU over serial

Example Client

A simple client application that periodically polls for some Coils

use rodbus::prelude::*;

use std::net::SocketAddr;
use std::time::Duration;
use std::str::FromStr;


use tokio::time::delay_for;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

   let channel = spawn_tcp_client_task(
       SocketAddr::from_str("127.0.0.1:502")?,
       10,
       strategy::default()
   );

   let mut session = channel.create_session(
       UnitId::new(0x02),
       Duration::from_secs(1)
   );

   // try to poll for some coils every 3 seconds
   loop {
       match session.read_coils(AddressRange::new(0, 5)).await {
           Ok(values) => {
               for x in values {
                   println!("index: {} value: {}", x.index, x.value)
               }
           },
           Err(err) => println!("Error: {:?}", err)
       }

       delay_for(std::time::Duration::from_secs(3)).await
   }
}

Example Server

use std::net::SocketAddr;
use std::str::FromStr;

use tokio::net::TcpListener;
use rodbus::prelude::*;

struct CoilsOnlyHandler {
   pub coils: [bool; 10]
}

impl CoilsOnlyHandler {
   fn new() -> Self {
       Self {
           coils: [false; 10]
       }
   }
}

impl ServerHandler for CoilsOnlyHandler {
   fn read_coils(&mut self, range: AddressRange) -> Result<&[bool], details::ExceptionCode> {
       Self::get_range_of(self.coils.as_ref(), range)
   }
}

#[tokio::main(threaded_scheduler)]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

   let handler = CoilsOnlyHandler::new().wrap();

   // map unit ids to a handler for processing requests
   let map = ServerHandlerMap::single(UnitId::new(1), handler.clone());

   // spawn a server to handle connections onto its own task
   rodbus::server::spawn_tcp_server_task(
       1,
       TcpListener::bind(SocketAddr::from_str("127.0.0.1:502")?).await?,
       map,
   );

   let mut next = tokio::time::Instant::now();

   // toggle all coils every couple of seconds
   loop {
       next += tokio::time::Duration::from_secs(2);
       {
           let mut guard = handler.lock().await;
           for c in &mut guard.coils {
               *c = !*c;
           }
       }
       tokio::time::delay_until(next).await;
   }
}

Modules

client

client API

constants

public constant values related to the Modbus specification

error

error types associated with making requests

prelude

prelude used to include all of the API types

server

server API

types

types used in requests and responses