Skip to main content

Crate rs_modbus

Crate rs_modbus 

Source
Expand description

A pure Rust implementation of MODBUS protocol.

rs-modbus is designed as a layered architecture, including the physical layer and the application layer:

  • Physical layer implements Serial Port, TCP/IP and UDP/IP.
  • Application layer implements RTU, ASCII and TCP.

Both client (master) and server (slave) are provided.

§Features

  • Full Modbus standard protocol implementation
  • Support for custom function codes
  • Support broadcasting
  • Very lightweight project

§Supported function codes

CodeName
01Read Coils
02Read Discrete Inputs
03Read Holding Registers
04Read Input Registers
05Write Single Coil
06Write Single Register
15Write Multiple Coils
16Write Multiple Registers
17Report Server ID
22Mask Write Register
23Read/Write Multiple Registers
43/14Read Device Identification

§Supported protocols

  • Modbus RTU
  • Modbus ASCII
  • Modbus TCP/IP
  • Modbus UDP/IP
  • Modbus RTU/ASCII Over TCP/IP
  • Modbus RTU/ASCII Over UDP/IP

§Examples

§Modbus TCP Master

use rs_modbus::layers::application::TcpApplicationLayer;
use rs_modbus::layers::physical::TcpClientPhysicalLayer;
use rs_modbus::master::{ModbusMaster, ModbusMasterOptions};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let physical = TcpClientPhysicalLayer::new();
    let application = TcpApplicationLayer::new(physical.clone());
    let master = ModbusMaster::new(
        application,
        physical,
        ModbusMasterOptions {
            timeout_ms: 5000,
            concurrent: false,
        },
    );

    master.open(None).await?;
    let res = master.read_holding_registers(1, 0, 10, None).await?;
    println!("{:?}", res.map(|r| r.data));
    master.destroy().await;

    Ok(())
}

§Modbus TCP Slave

use rs_modbus::layers::application::TcpApplicationLayer;
use rs_modbus::layers::physical::TcpServerPhysicalLayer;
use rs_modbus::slave::{ModbusSlave, ModbusSlaveModel};
use rs_modbus::types::AddressRange;
use async_trait::async_trait;

struct SimpleModel;

#[async_trait]
impl ModbusSlaveModel for SimpleModel {
    fn unit(&self) -> u8 { 1 }
    fn address_range(&self) -> AddressRange {
        AddressRange::default()
    }
    async fn read_holding_registers(
        &self, _address: u16, length: u16,
    ) -> Result<Vec<u16>, rs_modbus::error::ModbusError> {
        Ok(vec![0; length as usize])
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let physical = TcpServerPhysicalLayer::new();
    let application = TcpApplicationLayer::new(physical.clone());
    let slave = ModbusSlave::new(application, physical);

    slave.add(Box::new(SimpleModel));
    slave.open(None).await?;

    Ok(())
}

Re-exports§

pub use error::get_code_by_error;
pub use error::get_error_by_code;
pub use error::ErrorCode;
pub use error::ModbusError;
pub use layers::application::ApplicationLayer;
pub use layers::application::ApplicationProtocol;
pub use layers::application::ApplicationRole;
pub use layers::application::Framing;
pub use layers::physical::ConnectionId;
pub use layers::physical::DataEvent;
pub use layers::physical::PhysicalLayer;
pub use layers::physical::PhysicalLayerType;
pub use layers::physical::ResponseFn;
pub use master::ModbusMaster;
pub use master::ModbusMasterOptions;
pub use master_session::MasterSession;
pub use master_session::PreCheck;
pub use master_session::PreCheckOutcome;
pub use slave::ModbusSlave;
pub use slave::ModbusSlaveModel;
pub use slave::ModbusSlaveOptions;
pub use types::AddressRange;
pub use types::ApplicationDataUnit;
pub use types::CustomFunctionCode;
pub use types::DeviceIdentification;
pub use types::DeviceObject;
pub use types::FramedDataUnit;
pub use types::MasterResponse;
pub use types::ServerId;
pub use vars::limits;
pub use vars::ConformityLevel;
pub use vars::FunctionCode;
pub use vars::ReadDeviceIdCode;
pub use vars::COIL_OFF;
pub use vars::COIL_ON;
pub use vars::EXCEPTION_OFFSET;
pub use vars::MEI_READ_DEVICE_ID;

Modules§

error
layers
master
master_session
MasterSession — owns the in-flight “awaiting response” slots of a ModbusMaster. Multi-slot, keyed by WaiterKey: TCP requests key by their transaction ID (TID), FIFO/RTU/ASCII requests share the WaiterKey::Fifo slot since they have no TID to disambiguate by.
slave
types
utils
vars
Modbus protocol constants — function codes, exception offsets, MEI types, and PDU quantity limits. Mirrors njs-modbus vars.ts.