欢迎光临
我们一直在努力

Rust 实现异步ModbusRTU 主机源码分享

Rust 实现异步ModbusRTU 主机源码分享

  • 一、源码分享
    • 1、分享源码
    • 2、效果展示
  • 二、ModbusRTU协议详解
    • 1、ModbusRTU 协议概述
    • 2、 ModbusRTU 帧结构
      • 2.1 、帧格式详解
      • 2.2、 帧间隔要求
    • 3、常用功能码详解
      • 3.1 、线圈操作(Coils)
        • 0x01:读线圈
        • 0x05:写单个线圈
      • 3.2 寄存器操作(Registers)
        • 0x03:读保持寄存器
        • 0x06:写单个寄存器
      • 3.3 批量操作
        • 0x0F:写多个线圈
        • 0x10:写多个寄存器
    • 4、CRC-16 校验算法
    • 5、通信参数配置
    • 6、 错误处理与异常码
    • 7、ModbusRTU vs ModbusTCP 对比
    • 8、实际应用示例
      • 8.1 、读取温度传感器数据
      • 8.2、 控制继电器输出
    • 9、 调试工具推荐
    • 10.、注意事项

在这里插入图片描述

一、源码分享

1、分享源码

use tokio_serial::SerialStream;

use tokio_modbus::prelude::*;

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

let tty_path = "COM1";
let slave = Slave(1);

let builder = tokio_serial::new(tty_path, 19200);
let port = SerialStream::open(&builder).unwrap();

let mut ctx = rtu::attach_slave(port, slave);
loop {
let function_code = 0x03; // 读取保持寄存器的功能码
match ctx.read_holding_registers(0, 10).await {
Ok(values) => {
println!("功能码: 0x{:02X}, 寄存器值: {:?}", function_code, values);
// 在这里处理读取到的寄存器值
}
Err(err) => {
println!("功能码: 0x{:02X}, 读取寄存器失败: {:?}", function_code, err);
// 在这里处理错误
}
}

// 添加适当的延迟,避免过于频繁的请求
tokio::time::sleep(std::time::Duration::from_millis(300)).await;
}

ctx.disconnect().await?;

Ok(())
}

toml

[package]
name = "ttttest"
version = "0.1.0"
edition = "2024"

[dependencies]
futures = "0.3.31"
tokio = { version = "1.47.1", features = ["full"] }
anyhow = "1.0"
tokiomodbus = { version = "0.17.0", features = ["tcp", "server", "rtu","tcp-server"] }

2、效果展示

在这里插入图片描述

二、ModbusRTU协议详解

1、ModbusRTU 协议概述

ModbusRTU(Remote Terminal Unit)是 Modbus 协议的一种串行通信实现方式,采用二进制数据格式,通过 RS-232、RS-422 或 RS-485 等物理层进行传输。相比 ModbusTCP,RTU 版本更适用于工业现场环境,具有传输效率高、数据包紧凑的特点。

主要特性:

  • 传输方式:主从式(Master-Slave)通信
  • 数据格式:二进制(十六进制)
  • 校验方式:CRC-16 循环冗余校验
  • 帧间隔:至少 3.5 个字符时间的静默间隔作为帧分隔
  • 地址范围:从站地址 1-247(0 为广播地址)

2、 ModbusRTU 帧结构

一个完整的 ModbusRTU 帧由以下部分组成:

[从站地址] [功能码] [数据] [CRC校验]

2.1 、帧格式详解

字段长度说明
从站地址 1字节 目标设备地址(1-247)
功能码 1字节 指定要执行的操作类型
数据 N字节 具体操作参数和数据
CRC校验 2字节 循环冗余校验,低字节在前

2.2、 帧间隔要求

  • 帧间间隔:至少 3.5 个字符时间的静默
  • 字符间间隔:最大 1.5 个字符时间
  • 如果间隔超过 3.5 个字符时间,接收方认为新帧开始

3、常用功能码详解

3.1 、线圈操作(Coils)

0x01:读线圈

请求帧:

[地址][01][起始地址高][起始地址低][线圈数量高][线圈数量低][CRC低][CRC高]

响应帧:

[地址][01][字节数][线圈状态字节1][线圈状态字节2]…[CRC低][CRC高]

0x05:写单个线圈

请求帧:

[地址][05][输出地址高][输出地址低][输出值高][输出值低][CRC低][CRC高]

  • 输出值:FF00 表示 ON,0000 表示 OFF

3.2 寄存器操作(Registers)

0x03:读保持寄存器

请求帧:

[地址][03][起始地址高][起始地址低][寄存器数量高][寄存器数量低][CRC低][CRC高]

响应帧:

[地址][03][字节数][寄存器值高][寄存器值低]…[CRC低][CRC高]

0x06:写单个寄存器

请求帧:

[地址][06][寄存器地址高][寄存器地址低][寄存器值高][寄存器值低][CRC低][CRC高]

3.3 批量操作

0x0F:写多个线圈

请求帧:

[地址][0F][起始地址高][起始地址低][线圈数量高][线圈数量低][字节数][线圈状态字节1][线圈状态字节2]…[CRC低][CRC高]

0x10:写多个寄存器

请求帧:

[地址][10][起始地址高][起始地址低][寄存器数量高][寄存器数量低][字节数][寄存器值高][寄存器值低]…[CRC低][CRC高]

4、CRC-16 校验算法

ModbusRTU 使用 CRC-16(多项式:0x8005)进行数据校验,计算步骤如下:

// Rust 实现的 CRC-16 Modbus 校验函数
fn crc16_modbus(data: &[u8]) -> u16 {
let mut crc: u16 = 0xFFFF;

for &byte in data {
crc ^= byte as u16;

for _ in 0..8 {
if (crc & 0x0001) != 0 {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}

crc
}

// 使用示例
let frame = vec![0x01, 0x03, 0x00, 0x00, 0x00, 0x02];
let crc = crc16_modbus(&frame);
println!("CRC: {:04X}", crc); // 输出:C40B

5、通信参数配置

ModbusRTU 需要配置以下通信参数:

参数常用值说明
波特率 9600, 19200, 38400, 115200 通信速率
数据位 8 bits 数据位数
停止位 1 bit, 2 bits 停止位数
校验位 None, Even, Odd 奇偶校验
流控制 None 通常不使用

典型配置:

  • 波特率:9600 bps
  • 数据位:8 bits
  • 停止位:1 bit
  • 校验位:None(无校验)

6、 错误处理与异常码

当从站检测到错误时,会返回异常响应帧:

[地址][功能码 + 0x80][异常码][CRC低][CRC高]

常见异常码:

  • 0x01:非法功能码(不支持的功能)
  • 0x02:非法数据地址(地址不可用)
  • 0x03:非法数据值(数据值超出范围)
  • 0x04:从站设备故障(执行失败)

7、ModbusRTU vs ModbusTCP 对比

特性ModbusRTUModbusTCP
物理层 RS-232/RS-485 Ethernet
数据格式 二进制 TCP/IP 报文
寻址方式 从站地址 IP地址 + 单元标识符
校验方式 CRC-16 TCP 校验和
传输效率 高(无协议头开销) 较低(有TCP/IP头)
传输距离 较短(<1200m) 远(局域网/互联网)
适用场景 工业现场、设备层 监控层、系统集成

8、实际应用示例

8.1 、读取温度传感器数据

假设温度传感器地址为 1,温度值存储在保持寄存器 30001(对应 Modbus 地址 0x00):

请求帧(主机→从站):

01 03 00 00 00 01 84 0A

  • 01:从站地址
  • 03:读保持寄存器功能码
  • 00 00:起始地址 0x0000
  • 00 01:读取 1 个寄存器
  • 84 0A:CRC 校验

响应帧(从站→主机):

01 03 02 00 64 B9 9C

  • 01:从站地址
  • 03:功能码
  • 02:字节数(2字节)
  • 00 64:温度值 100(0.1℃ 分辨率 = 10.0℃)
  • B9 9C:CRC 校验

8.2、 控制继电器输出

控制地址为 2 的设备的线圈 00001(对应 Modbus 地址 0x00):

请求帧(写单个线圈 ON):

02 05 00 00 FF 00 8C 3A

  • 02:从站地址
  • 05:写单个线圈功能码
  • 00 00:线圈地址 0x0000
  • FF 00:输出值 ON
  • 8C 3A:CRC 校验

9、 调试工具推荐

  • Modbus Poll:功能强大的 Modbus 主站模拟软件
  • Modbus Slave:Modbus 从站模拟软件
  • QModMaster:开源的 Modbus 主站工具
  • Serial Port Monitor:串口数据监视工具
  • Wireshark:网络协议分析(支持 ModbusTCP)
  • 10.、注意事项

  • 地址映射:注意 Modbus 地址与设备实际地址的映射关系
  • 超时设置:根据网络环境合理设置通信超时
  • 重试机制:实现通信失败时的重试逻辑
  • 并发控制:避免多个主站同时访问同一从站
  • 数据对齐:注意字节序(大端序/小端序)问题
  • 在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » Rust 实现异步ModbusRTU 主机源码分享
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址