Modbus协议指南---个人学习笔记

文章目录

    • [1. Modbus协议概述](#1. Modbus协议概述)
    • [1.1 什么是Modbus协议](#1.1 什么是Modbus协议)
    • [1.2 Modbus协议的发展历程](#1.2 Modbus协议的发展历程)
  • [2. Modbus协议类型详解](#2. Modbus协议类型详解)
    • [2.1 Modbus-RTU](#2.1 Modbus-RTU)
      • [2.1.1 RTU帧结构](#2.1.1 RTU帧结构)
      • [2.1.2 RTU帧边界识别](#2.1.2 RTU帧边界识别)
      • [2.1.3 主从通讯流程](#2.1.3 主从通讯流程)
    • [2.2 Modbus-ASCII](#2.2 Modbus-ASCII)
      • [2.2.1 ASCII帧结构](#2.2.1 ASCII帧结构)
    • [2.3 Modbus-TCP](#2.3 Modbus-TCP)
      • [2.3.1 TCP帧结构](#2.3.1 TCP帧结构)
    • [2.4 Modbus Plus(ModbusPlus / MB+)](#2.4 Modbus Plus(ModbusPlus / MB+))
    • [2.5 三种主流模式对比](#2.5 三种主流模式对比)
  • [3. Modbus数据模型](#3. Modbus数据模型)
    • [3.1 四种数据类型](#3.1 四种数据类型)
      • [3.1.1 线圈(Coils)- 0区](#3.1.1 线圈(Coils)- 0区)
      • [3.1.2 离散输入(Discrete Inputs)- 1区](#3.1.2 离散输入(Discrete Inputs)- 1区)
      • [3.1.3 输入寄存器(Input Registers)- 3区](#3.1.3 输入寄存器(Input Registers)- 3区)
      • [3.1.4 保持寄存器(Holding Registers)- 4区](#3.1.4 保持寄存器(Holding Registers)- 4区)
    • [3.2 数据类型汇总表](#3.2 数据类型汇总表)
    • [3.3 地址系统详解](#3.3 地址系统详解)
      • [3.3.1 两种地址表示方式](#3.3.1 两种地址表示方式)
      • [3.3.2 地址转换规则](#3.3.2 地址转换规则)
  • [4. Modbus功能码详解](#4. Modbus功能码详解)
    • [4.1 功能码概述](#4.1 功能码概述)
    • [4.2 基础读写类功能码(0x01-0x04/0x05/0x06/0x0F/0x10)](#4.2 基础读写类功能码(0x01-0x04/0x05/0x06/0x0F/0x10))
      • [4.2.1 功能码01:读线圈(Read Coils)](#4.2.1 功能码01:读线圈(Read Coils))
      • [4.2.2 功能码02:读离散输入(Read Discrete Inputs)](#4.2.2 功能码02:读离散输入(Read Discrete Inputs))
      • [4.2.3 功能码03:读保持寄存器(Read Holding Registers)](#4.2.3 功能码03:读保持寄存器(Read Holding Registers))
      • [4.2.4 功能码04:读输入寄存器(Read Input Registers)](#4.2.4 功能码04:读输入寄存器(Read Input Registers))
      • [4.2.5 功能码05:写单个线圈(Write Single Coil)](#4.2.5 功能码05:写单个线圈(Write Single Coil))
      • [4.2.6 功能码06:写单个保持寄存器(Write Single Register)](#4.2.6 功能码06:写单个保持寄存器(Write Single Register))
      • [4.2.7 功能码15(0x0F):写多个线圈(Write Multiple Coils)](#4.2.7 功能码15(0x0F):写多个线圈(Write Multiple Coils))
      • [4.2.8 功能码16(0x10):写多个保持寄存器(Write Multiple Registers)](#4.2.8 功能码16(0x10):写多个保持寄存器(Write Multiple Registers))
    • [4.3 进阶控制类功能码(0x15-0x16)](#4.3 进阶控制类功能码(0x15-0x16))
      • [4.3.1 功能码21(0x15):强制多点线圈](#4.3.1 功能码21(0x15):强制多点线圈)
      • [4.3.2 功能码22(0x16):预置单个寄存器](#4.3.2 功能码22(0x16):预置单个寄存器)
    • [4.4 诊断维护类功能码(0x07-0x08/0x14)](#4.4 诊断维护类功能码(0x07-0x08/0x14))
      • [4.4.1 功能码07:读异常状态](#4.4.1 功能码07:读异常状态)
      • [4.4.2 功能码08:诊断](#4.4.2 功能码08:诊断)
      • [4.4.3 功能码20(0x14):读从站ID](#4.4.3 功能码20(0x14):读从站ID)
    • [4.5 功能码速查表](#4.5 功能码速查表)
    • [4.6 异常响应](#4.6 异常响应)
  • 5.Modbus校验算法详解
    • [5.1 CRC-16校验算法(RTU模式)](#5.1 CRC-16校验算法(RTU模式))
      • [5.1.1 CRC校验原理](#5.1.1 CRC校验原理)
      • [5.1.2 CRC计算算法(C语言实现)](#5.1.2 CRC计算算法(C语言实现))
      • [5.1.3 CRC快速验证](#5.1.3 CRC快速验证)
    • [5.2 LRC校验算法(ASCII模式)](#5.2 LRC校验算法(ASCII模式))
      • [5.2.1 LRC校验原理](#5.2.1 LRC校验原理)
      • [5.2.2 LRC计算算法(C语言实现)](#5.2.2 LRC计算算法(C语言实现))
    • [5.3 校验算法对比](#5.3 校验算法对比)
  • [6. 数据解析方案](#6. 数据解析方案)
    • [6.1 32位浮点数解析](#6.1 32位浮点数解析)
      • [6.1.1 IEEE754浮点数解析](#6.1.1 IEEE754浮点数解析)
      • [6.1.2 字节序问题](#6.1.2 字节序问题)
    • [6.2 BCD码解析](#6.2 BCD码解析)
    • [6.3 数据类型转换示例](#6.3 数据类型转换示例)
  • [7. 物理接口详解与接线规范](#7. 物理接口详解与接线规范)
    • [7.1 RS-232与RS-485接口对比](#7.1 RS-232与RS-485接口对比)
      • [7.1.1 RS-232接口](#7.1.1 RS-232接口)
      • [7.1.2 RS-485接口](#7.1.2 RS-485接口)
      • [7.1.3 RS-232 vs RS-485 对比总结](#7.1.3 RS-232 vs RS-485 对比总结)
    • [7.2 RS-485接线规范](#7.2 RS-485接线规范)
      • [7.2.1 接线方式](#7.2.1 接线方式)
      • [7.2.2 终端电阻设置](#7.2.2 终端电阻设置)
    • [7.3 通信参数设置](#7.3 通信参数设置)
      • [7.3.1 串口参数](#7.3.1 串口参数)
      • [7.3.2 通信超时设置](#7.3.2 通信超时设置)
    • [7.4 批量操作限制](#7.4 批量操作限制)
  • 8.Modbus实战案例
    • [8.1 案例1:PLC控制变频器完整流程](#8.1 案例1:PLC控制变频器完整流程)
    • [8.2 案例2:读取电力仪表数据](#8.2 案例2:读取电力仪表数据)
    • [8.3 案例3:批量控制照明回路](#8.3 案例3:批量控制照明回路)
    • [8.4 案例4:PLC程序实现(SCL语言)](#8.4 案例4:PLC程序实现(SCL语言))
  • [9. 调试技巧与工具推荐](#9. 调试技巧与工具推荐)
    • [9.1 调试排查顺序](#9.1 调试排查顺序)
    • [9.2 常见问题排查](#9.2 常见问题排查)
      • [9.2.1 通信超时](#9.2.1 通信超时)
      • [9.2.2 校验错误](#9.2.2 校验错误)
      • [9.2.3 异常响应](#9.2.3 异常响应)
    • [9.3 调试工具推荐](#9.3 调试工具推荐)
      • [9.3.1 PC端工具](#9.3.1 PC端工具)
      • [9.3.2 Wireshark抓包分析Modbus TCP](#9.3.2 Wireshark抓包分析Modbus TCP)
      • [9.3.3 在线工具](#9.3.3 在线工具)
      • [9.3.4 手机端工具](#9.3.4 手机端工具)
  • 10.Modbus协议进阶
    • [10.1 Modbus网关](#10.1 Modbus网关)
    • [10.2 Modbus安全](#10.2 Modbus安全)
      • [10.2.1 安全风险分析](#10.2.1 安全风险分析)
      • [10.2.2 安全防护措施](#10.2.2 安全防护措施)
      • [10.2.3 Modbus Secure(下一代安全方案)](#10.2.3 Modbus Secure(下一代安全方案))
    • [10.3 协议演进:Modbus vs OPC UA](#10.3 协议演进:Modbus vs OPC UA)
    • [10.4 Modbus与物联网(IoT)集成](#10.4 Modbus与物联网(IoT)集成)
      • [10.4.1 Modbus over MQTT架构](#10.4.1 Modbus over MQTT架构)
      • [10.4.2 常见IoT网关方案](#10.4.2 常见IoT网关方案)
      • [10.4.3 Node.js实现示例](#10.4.3 Node.js实现示例)
      • [10.4.4 Python pymodbus完整示例](#10.4.4 Python pymodbus完整示例)
  • 11.学习资源汇总
    • [11.1 官方规范文档](#11.1 官方规范文档)
    • [11.2 优质教程链接](#11.2 优质教程链接)
      • [11.2.1 基础教程](#11.2.1 基础教程)
      • [11.2.2 进阶教程](#11.2.2 进阶教程)
      • [11.2.3 实战教程](#11.2.3 实战教程)
    • [11.3 在线工具](#11.3 在线工具)
    • [11.4 开发库推荐](#11.4 开发库推荐)
      • [11.4.1 Python库](#11.4.1 Python库)
      • [11.4.2 C/C++库](#11.4.2 C/C++库)
  • 12.常见问题解答(FAQ)
  • [13. 核心总结](#13. 核心总结)
    • [13.1 三角逻辑](#13.1 三角逻辑)
    • [13.2 调试排查顺序](#13.2 调试排查顺序)
    • [13.3 关键记忆点](#13.3 关键记忆点)
    • [13.4 工程师实战经验总结](#13.4 工程师实战经验总结)
      • [13.4.1 为什么Modbus经久不衰?](#13.4.1 为什么Modbus经久不衰?)
      • [13.4.2 学习路径建议](#13.4.2 学习路径建议)
      • [13.4.3 常见"坑"整理](#13.4.3 常见"坑"整理)
      • [13.4.4 主流设备Modbus接入要点](#13.4.4 主流设备Modbus接入要点)
  • 14.附录
  • 15.参考资料

1. Modbus协议概述

1.1 什么是Modbus协议

Modbus协议是一种串行通信协议 ,由Modicon公司(现为施耐德电气Schneider Electric)于1979年开发,最初用于可编程逻辑控制器(PLC)之间的通信。经过四十余年的发展,Modbus已经成为工业领域应用最广泛的通信协议之一,被誉为工业通信的"通用语言"。

Modbus RTU采用半双工主从应答机制,是PLC、变频器、传感器等设备互联的核心协议。

核心特点

特性 说明
开放性强 协议完全公开,无版权限制,任何厂商都可以免费实现
简单易用 协议设计简洁,学习门槛低,便于快速开发部署
兼容性好 支持多种物理层接口,适应不同的硬件环境
稳定性高 在恶劣工业环境下长期运行稳定可靠
生态丰富 全球数以万计的设备支持Modbus协议

1.2 Modbus协议的发展历程

时间节点 里程碑事件
1979年 Modicon公司发布Modbus协议,用于PLC间通信
1980年代 Modbus RTU成为工业串行通信的事实标准
1990年代 以太网普及,Modbus TCP应运而生
2004年 Modbus-IDA组织成立,推动协议标准化
至今 成为全球工业通信领域最流行的协议之一

2. Modbus协议类型详解

Modbus协议主要分为三种传输模式:Modbus-RTUModbus-ASCIIModbus-TCP。三种模式各有特点,适用于不同的应用场景。

2.1 Modbus-RTU

Modbus-RTU是最常用的传输模式,采用二进制编码方式,传输效率最高。

2.1.1 RTU帧结构

复制代码
┌─────────┬────────────┬──────────────┬─────────┬─────────┐
│ 地址域   │ 功能码域    │ 数据域        │ CRC低位 │ CRC高位 │
│ 1字节    │ 1字节      │ N字节         │ 1字节   │ 1字节   │
└─────────┴────────────┴──────────────┴─────────┴─────────┘

帧结构详细说明

字段 字节数 关键说明 实战注意事项
从站地址 1 0=广播(无响应),1-247=唯一设备地址 地址冲突会导致通讯瘫痪,需用调试工具扫描排查
功能码 1 指令核心,异常时最高位设1(0x03→0x83) 错误功能码直接触发异常响应帧
数据字段 N 含地址、数值、长度等,随功能码动态变化 字节序需与设备手册一致(大端/小端)
CRC校验 2 前N+2字节的CRC-16校验,低字节优先 校验错误无响应,需用在线工具验证

2.1.2 RTU帧边界识别

RTU模式通过**静默时间(T3.5)**来识别帧边界:

复制代码
 帧与帧之间至少需要3.5个字符时间的静默间隔
          ↓
┌─────────┐   ←T3.5→   ┌─────────┐   ←T3.5→   ┌─────────┐
│  帧1    │ ─────────  │  帧2    │ ─────────  │  帧3    │
└─────────┘            └─────────┘            └─────────┘

字符时间计算公式

复制代码
Tchar = (数据位 + 起始位 + 停止位 + 校验位) / 波特率
T3.5 = 3.5 × Tchar

示例(波特率9600,8N1格式):

  • Tchar = 10 / 9600 ≈ 1.04ms
  • T3.5 ≈ 3.65ms

2.1.3 主从通讯流程

复制代码
┌──────────┐                              ┌──────────┐
│   主站   │  ──── 发送请求帧 ──────────> │   从站   │
│ (Master) │                              │ (Slave)  │
│          │  <──── 返回响应帧 ────────── │          │
└──────────┘                              └──────────┘

通讯规则:
1. 主站发送含目标地址的请求帧
2. 匹配地址的从站校验CRC,执行指令后返回响应帧
3. 地址不匹配或CRC错误时,从站无任何反馈

2.2 Modbus-ASCII

Modbus-ASCII采用ASCII字符编码,便于人工调试和问题排查。

2.2.1 ASCII帧结构

复制代码
┌──────┬─────────┬────────────┬──────────────┬───────┬───────┐
│ 起始 │ 地址域   │ 功能码域    │ 数据域        │ LRC   │ 结束  │
│ ':'  │ 2字符   │ 2字符      │ 2×N字符      │ 2字符 │ CR LF │
└──────┴─────────┴────────────┴──────────────┴───────┴───────┘

帧结构说明

字段 长度 说明
起始符 1字符 固定为冒号':'(0x3A)
地址域 2字符 从站地址的ASCII十六进制表示
功能码域 2字符 功能码的ASCII十六进制表示
数据域 2×N字符 数据的ASCII十六进制表示
LRC校验 2字符 LRC校验值的ASCII表示
结束符 2字符 回车换行(CR LF,0x0D 0x0A)

2.3 Modbus-TCP

Modbus-TCP是基于以太网的传输模式,支持远程访问和并发通信。

2.3.1 TCP帧结构

复制代码
┌───────────────────── MBAP报文头 ────────────────────┬────────── PDU ───────────┐
│                                                      │                          │
┌────────────┬────────────┬────────────┬─────────────┬────────────┬────────────────┐
│ 事务标识符  │ 协议标识符  │ 长度字段   │ 单元标识符  │ 功能码域    │ 数据域         │
│ 2字节      │ 2字节      │ 2字节     │ 1字节      │ 1字节      │ N字节          │
└────────────┴────────────┴────────────┴─────────────┴────────────┴────────────────┘

MBAP报文头说明

字段 长度 说明
事务标识符 2字节 用于请求/响应匹配,客户端生成
协议标识符 2字节 固定为0x0000(Modbus协议)
长度字段 2字节 后续字节数(单元标识符+PDU)
单元标识符 1字节 从站地址或网关单元标识

2.4 Modbus Plus(ModbusPlus / MB+)

Modbus Plus是Modicon公司推出的一种高速令牌环网络协议,专为工业自动化环境设计。

主要特性

特性 说明
传输速率 1Mbps
拓扑结构 令牌环,节点间对等通信
最大节点数 64个
通信方式 点对点 + 广播
应用场景 高速工业控制网络(现已较少使用)

⚠️ 注意:Modbus Plus为Modicon私有协议,与标准Modbus RTU/ASCII/TCP不兼容。目前工业界普遍使用RTU和TCP,Modbus Plus已逐渐被淘汰。

2.5 三种主流模式对比

对比项 Modbus-RTU Modbus-ASCII Modbus-TCP
编码方式 二进制 ASCII字符 二进制
传输效率 ★★★★★ ★★ ★★★★★★
有效数据率 ~85% ~40% ~95%
校验方式 CRC-16 LRC TCP协议
物理介质 RS-485/RS-232 RS-485/RS-232 以太网
传输距离 ≤1200m ≤1200m 无限制
最大节点 247个 247个 无限制
响应时间 40-100ms 80-200ms 5-20ms
调试难度 中等 简单 中等
应用场景 现场控制 设备调试 远程监控

3. Modbus数据模型

3.1 四种数据类型

Modbus定义了四种基本数据类型,对应不同的存储区域:

很多初学者对"0区、1区、3区、4区"的编号感到困惑------为什么没有2区?这是Modbus协议历史遗留的编号规则:
存储区编号的命名逻辑

区号 存储区名称 逻辑地址前缀 命名原因
0区 输出线圈(可读写) 0xxxx 原始规范中输出线圈排第0位
1区 输入线圈(只读离散输入) 1xxxx 数字输入排第1位
3区 输入寄存器(只读) 3xxxx 模拟输入排第3位(2区在扩展规范中预留)
4区 保持寄存器(可读写) 4xxxx 可配置寄存器排第4位

💡 记忆技巧

  • 线圈 = 布尔量(开/关),类比PLC的I/Q/M区
  • 寄存器 = 数值量(整数/浮点),类比PLC的V/T/C/DB区
  • 带"输入"的 = 只读 ;不带"输入"的 = 可读写

3.1.1 线圈(Coils)- 0区

定义:可读写的单比特数据,用于表示开关量输出。

属性 说明
地址前缀 0xxxx
地址范围 00001-09999
数据位数 1位(布尔值)
读写权限 读/写
典型应用 继电器控制、阀门开关、指示灯、变频器控制

3.1.2 离散输入(Discrete Inputs)- 1区

定义:只读的单比特数据,用于表示开关量输入。

属性 说明
地址前缀 1xxxx
地址范围 10001-19999
数据位数 1位(布尔值)
读写权限 只读
典型应用 按钮状态、行程开关、限位开关、传感器信号

3.1.3 输入寄存器(Input Registers)- 3区

定义:只读的16位数据,用于存储模拟量输入值。

属性 说明
地址前缀 3xxxx
地址范围 30001-39999
数据位数 16位(1个字)
读写权限 只读
典型应用 温度、压力、流量传感器数据、变频器实际转速

3.1.4 保持寄存器(Holding Registers)- 4区

定义:可读写的16位数据,用于存储参数设置和运行数据。

属性 说明
地址前缀 4xxxx
地址范围 40001-49999
数据位数 16位(1个字)
读写权限 读/写
典型应用 设定值、PID参数、报警阈值、变频器参数

3.2 数据类型汇总表

数据类型 地址前缀 地址范围 位数 读写 功能码(读) 功能码(写)
线圈 0xxxx 00001-09999 1位 读/写 01 05/15
离散输入 1xxxx 10001-19999 1位 只读 02 -
输入寄存器 3xxxx 30001-39999 16位 只读 04 -
保持寄存器 4xxxx 40001-49999 16位 读/写 03 06/16

3.3 地址系统详解

3.3.1 两种地址表示方式

1. 逻辑地址(参考地址)

这是设备手册中常用的表示方式:

复制代码
40001 → 第1个保持寄存器
40002 → 第2个保持寄存器
30001 → 第1个输入寄存器
00001 → 第1个线圈

2. 协议地址(偏移地址)

这是实际通信中使用的地址,从0开始:

复制代码
逻辑地址40001 → 协议地址0x0000
逻辑地址40002 → 协议地址0x0001
逻辑地址30001 → 协议地址0x0000

3.3.2 地址转换规则

地址映射速算

复制代码
协议地址 = 逻辑地址 - 地址区起始值

示例:
逻辑地址40010 → 协议地址 = 40010 - 40001 = 9 (0x0009)
逻辑地址30005 → 协议地址 = 30005 - 30001 = 4 (0x0004)
线圈00265 → 协议地址 = 265 - 1 = 264 (0x0108)

4. Modbus功能码详解

4.1 功能码概述

功能码是Modbus协议的核心,用于指定要执行的操作类型。

功能码范围分类

范围 类型
0x00-0x4F 公共功能码
0x50-0x7F 保留码
0x80-0xFF 异常响应码(功能码+0x80)

4.2 基础读写类功能码(0x01-0x04/0x05/0x06/0x0F/0x10)

4.2.1 功能码01:读线圈(Read Coils)

功能:读取从站中连续线圈的状态(ON/OFF)。

操作对象:线圈寄存器(00001-09999,1位读写)

报文格式

类型 字段 字节数 说明
请求 从站地址 1 目标设备地址
请求 功能码 1 0x01
请求 起始地址 2 线圈起始地址
请求 线圈数量 2 读取数量
请求 CRC校验 2 CRC-16
响应 从站地址 1 设备地址
响应 功能码 1 0x01
响应 返回字节数 1 数据字节数
响应 线圈状态数据 N 线圈状态
响应 CRC校验 2 CRC-16

变频器案例:读取V20变频器故障指示灯(线圈00001-00004)

复制代码
请求帧:01 01 00 00 00 04 79 CB
       │  │  └──┬──┘ └──┬──┘ └CRC
       │  │     │      └─读取4个线圈
       │  │     └─起始地址:00001
       │  └─功能码:01
       └─从站地址:01

响应帧:01 01 01 05 E1 8C
       │  │  │  └─线圈数据:0x05(二进制0101)
       │  │  └─数据字节数:1
       │  └─功能码:01
       └─从站地址:01

数据解析:
0x05 = 0000 0101(二进制)
线圈1 = ON(故障指示灯亮)
线圈2 = OFF
线圈3 = ON(运行指示灯亮)
线圈4 = OFF

4.2.2 功能码02:读离散输入(Read Discrete Inputs)

功能:读取从站中连续离散输入的状态。

操作对象:离散输入寄存器(10001-19999,1位只读)

v倾角传感器案例:读取4个限位输入(10001-10004)

复制代码
请求帧:03 02 00 00 00 04 79 C9
       └─从站03,读4个离散输入

响应帧:03 02 01 0F E1 8C
       └─数据:0x0F = 0000 1111(4个输入全有效,倾角超限)

4.2.3 功能码03:读保持寄存器(Read Holding Registers)

功能:读取从站中连续保持寄存器的值。这是最常用的功能码之一。

操作对象:保持寄存器(40001-49999,16位读写)

电力仪表案例:读取MMP-5000D有功电度(40001-40002)

复制代码
请求帧:01 03 00 00 00 02 C4 0B
       │  │  └──┬──┘ └──┬──┘ └CRC
       │  │     │      └─读取2个寄存器
       │  │     └─起始地址:0(对应40001)
       │  └─功能码:03
       └─从站地址:01

响应帧:01 03 04 00 0F 42 40 7A 1F
       │  │  │  └──────┬──────┘ └CRC
       │  │  │          └─32位浮点数:0x000F4240
       │  │  └─数据字节数:4(2个寄存器×2字节)
       │  └─功能码:03
       └─从站地址:01

数据解析:
0x000F4240(大端序)= 1000000
IEEE754浮点数转换 = 1000.0 kWh(电度值)

4.2.4 功能码04:读输入寄存器(Read Input Registers)

功能:读取从站中连续输入寄存器的值(只读)。

操作对象:输入寄存器(30001-39999,16位只读)

变频器案例:读取V20实际转速(30001)

复制代码
请求帧:02 04 00 00 00 01 90 0A
       └─从站02,读输入寄存器30001

响应帧:02 04 02 13 88 AD F3
       └─数据:0x1388 = 5000 → 50.0Hz(实际频率)

4.2.5 功能码05:写单个线圈(Write Single Coil)

功能:向从站写入单个线圈的值(ON/OFF)。

PLC案例:控制S7-1200继电器输出(线圈00001)

复制代码
写入ON:
请求帧:01 05 00 00 FF 00 CD 6B
       │  │  └──┬──┘ └──┬──┘ └CRC
       │  │     │      └─输出值:0xFF00(ON)
       │  │     └─输出地址:00001
       │  └─功能码:05
       └─从站地址:01

响应帧:01 05 00 00 FF 00 CD 6B(与请求相同)

写入OFF:
请求帧:01 05 00 00 00 00 CD CA
       └─输出值:0x0000(OFF)

4.2.6 功能码06:写单个保持寄存器(Write Single Register)

功能:向从站写入单个保持寄存器的值。

变频器案例:设置V20基准频率(保持寄存器40100)

复制代码
请求帧:02 06 00 63 13 88 2D F4
       │  │  └──┬──┘ └──┬──┘ └CRC
       │  │     │      └─写入值:0x1388 = 5000(50Hz)
       │  │     └─寄存器地址:99(对应40100)
       │  └─功能码:06
       └─从站地址:02

响应帧:02 06 00 63 13 88 2D F4(与请求相同)

4.2.7 功能码15(0x0F):写多个线圈(Write Multiple Coils)

功能:向从站写入多个线圈的值。

楼宇自控案例:控制8个照明回路(线圈00001-00008)

复制代码
请求帧:01 0F 00 00 00 08 01 55 73 0A
       │  │  └──┬──┘ └──┬──┘ │  └─┬─┘ └CRC
       │  │     │      └─8个 │    └─数据:0x55(0101 0101)
       │  │     └─起始地址   └1字节
       │  └─功能码:0F(15)
       └─从站地址:01

数据解析:0x55 = 0101 0101
线圈1/3/5/7 = ON(间隔开灯)
线圈2/4/6/8 = OFF

响应帧:01 0F 00 00 00 08 41 0A

4.2.8 功能码16(0x10):写多个保持寄存器(Write Multiple Registers)

功能:向从站写入多个保持寄存器的值。

Beckhoff案例:配置倾角传感器采样参数(40001-40002)

复制代码
请求帧:03 10 00 00 00 02 04 00 0A 00 05 B9 03
       │  │  └──┬──┘ └──┬──┘ │  └──────┬──────┘ └CRC
       │  │     │      └─2个 │         └─数据:10Hz, 5次平均
       │  │     └─起始地址   └4字节数据
       │  └─功能码:10(16)
       └─从站地址:03

响应帧:03 10 00 00 00 02 11 0A

4.3 进阶控制类功能码(0x15-0x16)

4.3.1 功能码21(0x15):强制多点线圈

功能:强制线圈置位/复位,区别于普通写入,具有更高优先级。

机床案例:紧急停止3个伺服轴(线圈00001-00003)

复制代码
请求帧:01 15 00 00 00 03 01 07 29 0A
       │  │  └──┬──┘ └──┬──┘ │  └─数据:0x07(111,全部ON)
       │  │     │      └─3个 │
       │  │     └─起始地址   └1字节
       │  └─功能码:15(21)
       └─从站地址:01

响应帧:01 15 00 00 00 03 69 0A

4.3.2 功能码22(0x16):预置单个寄存器

功能:优先级高于0x06,用于紧急参数修改。

光伏案例:调整逆变器功率限制(保持寄存器40200)

复制代码
请求帧:05 16 00 C8 07 D0 8F 0B
       │  │  └──┬──┘ └──┬──┘ └CRC
       │  │     │      └─写入值:0x07D0 = 2000W
       │  │     └─寄存器地址:200(对应40200)
       │  └─功能码:16(22)
       └─从站地址:05

响应帧:与请求帧一致

4.4 诊断维护类功能码(0x07-0x08/0x14)

4.4.1 功能码07:读异常状态

功能:快速获取设备故障状态。

复制代码
请求帧:02 07 00 00
响应帧:02 07 05 B3 21
        └─异常码:0x05(bit2=1代表通讯超时)

4.4.2 功能码08:诊断

功能:设备自检与复位。

常用子功能码

子功能码 说明
0x0001 硬件复位
0x0002 清除故障记录
0x0003 读取通信事件计数器

PLC案例:S7-1200通讯复位

复制代码
请求帧:01 08 00 01 00 00 00 00 79 0B
       └─子功能码:0x0001(硬件复位)

响应帧:01 08 00 01 00 00 00 00 F9 0B

4.4.3 功能码20(0x14):读从站ID

功能:查询设备型号和厂商信息。

复制代码
请求帧:04 14 00 00
响应帧:04 14 0A 56 32 30 2D 50 52 4F 20 20 20 A3 F2
        └─设备型号:"V20-PRO"(ASCII码)

4.5 功能码速查表

功能码 名称 操作对象 适用场景
0x01 读线圈 线圈寄存器 开关状态查询
0x02 读离散输入 离散输入寄存器 传感器信号检测
0x03 读保持寄存器 保持寄存器 参数读取
0x04 读输入寄存器 输入寄存器 模拟量采集
0x05 写单个线圈 线圈寄存器 单点控制
0x06 写单个保持寄存器 保持寄存器 参数整定
0x0F (15) 写多个线圈 线圈寄存器 批量开关控制
0x10 (16) 写多个保持寄存器 保持寄存器 批量参数配置
0x15 (21) 强制多点线圈 线圈寄存器 紧急控制
0x16 (22) 预置单个寄存器 保持寄存器 优先级参数修改
0x07 读异常状态 - 故障快速定位
0x08 诊断 - 设备自检与复位
0x14 (20) 读从站ID 设备信息区 设备型号查询

4.6 异常响应

当从站无法正常执行请求时,会返回异常响应。

异常响应帧格式

复制代码
┌────────┬──────────────┬──────────┬─────────┐
│ 从站地址 │ 功能码+0x80   │ 异常码   │ CRC校验 │
│ 1字节   │ 1字节        │ 1字节    │ 2字节   │
└────────┴──────────────┴──────────┴─────────┘

异常码详细说明

异常码 名称 说明 排查方案 报文案例
01 非法功能码 设备不支持该功能码 核对设备手册支持的功能码列表 01 83 01 D3 F8
02 非法数据地址 请求的地址不存在 确认寄存器地址范围(如40001-49999) 01 83 02 C3 51
03 非法数据值 请求数据超出范围 检查数值是否超出设备量程 01 85 03 92 0A
04 从站设备故障 从站执行时发生错误 重启设备或恢复出厂设置 01 86 04 33 B7
05 确认/忙 请求已接受正在处理 降低通讯频率或排查设备负载 02 86 05 23 C7
06 拒绝执行 设备拒绝执行请求 检查设备权限或锁定状态 03 8A 06 13 D7

异常响应示例

复制代码
请求读不存在的地址:
请求帧:01 03 FF FF 00 01
响应帧:01 83 02 C3 F1
        │  │  │  └CRC
        │  │  └─异常码:02(非法数据地址)
        │  └─功能码:83(03+0x80)
        └─从站地址:01

5.Modbus校验算法详解

5.1 CRC-16校验算法(RTU模式)

5.1.1 CRC校验原理

CRC(循环冗余校验)是一种基于多项式的校验算法。Modbus RTU使用CRC-16,多项式为0xA001。

CRC-16计算步骤

  1. 初始化CRC寄存器为0xFFFF
  2. 对每个字节进行计算:
    • 将字节与CRC低字节异或
    • 对结果进行8次位处理
  3. 最终CRC寄存器值即为校验结果

5.1.2 CRC计算算法(C语言实现)

c 复制代码
uint16_t CRC16(uint8_t *data, uint16_t length) {
    uint16_t crc = 0xFFFF;
    uint16_t i, j;

    for (i = 0; i < length; i++) {
        crc ^= data[i];
        for (j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }

    return crc;
}

5.1.3 CRC快速验证

使用在线CRC计算器:

  • 输入:01 03 00 00 00 02
  • 输出:CRC校验值 C4 0B(低字节在前)

5.2 LRC校验算法(ASCII模式)

5.2.1 LRC校验原理

LRC(纵向冗余校验)是一种简单的校验方法,计算所有字节的和的补码。

LRC计算步骤

  1. 将所有字节相加(忽略溢出)
  2. 取反加1(补码)
  3. 结果转换为两个ASCII字符

5.2.2 LRC计算算法(C语言实现)

c 复制代码
uint8_t LRC(uint8_t *data, uint16_t length) {
    uint8_t lrc = 0;
    uint16_t i;

    for (i = 0; i < length; i++) {
        lrc += data[i];
    }

    return (uint8_t)(-(int8_t)lrc);
}

5.3 校验算法对比

特性 CRC-16 LRC
校验位数 16位 8位
检错能力 强(99.99%) 较弱(99.6%)
计算复杂度 中等 简单
适用模式 RTU ASCII
算法 多项式0xA001 累加求补码

6. 数据解析方案

6.1 32位浮点数解析

Modbus寄存器为16位,32位浮点数需要两个连续寄存器存储。

6.1.1 IEEE754浮点数解析

案例:电力仪表电度值(40001-40002)

复制代码
响应帧:01 03 04 00 0F 42 40 7A 1F
数据:00 0F 42 40

字节序重组(大端序):
原始:0x00 0x0F 0x42 0x40
重组:0x000F4240

IEEE754转换:
0x000F4240 = 1000.0(kWh)

6.1.2 字节序问题

32位数据可能存在四种排列方式:

字节序 排列方式 示例(ABCD原序)
ABCD 大端序 00 0F 42 40
DCBA 小端序 40 42 0F 00
BADC 大端字交换 0F 00 40 42
CDAB 小端字交换 42 40 00 0F

处理方法

  1. 查看设备手册确认字节序
  2. 使用调试工具验证数据
  3. 编程时正确重组字节

6.2 BCD码解析

规则:每4位对应1位十进制数

案例:电表读数

复制代码
寄存器值:0x1234
解析:
高4位(0x12)→ 十位"12"
低4位(0x34)→ 个位"34"
结果:1234.0

注意事项:
- 每个半字节范围0-9,超过9为非法BCD码
- 常用于时间、日期、计数器等数据

6.3 数据类型转换示例

python 复制代码
# Python数据解析示例
import struct

# 32位浮点数解析(大端序)
def parse_float32_be(data):
    """大端序浮点数解析"""
    return struct.unpack('>f', data)[0]

# 32位浮点数解析(小端序)
def parse_float32_le(data):
    """小端序浮点数解析"""
    return struct.unpack('<f', data)[0]

# BCD码解析
def parse_bcd(value):
    """BCD码转十进制"""
    high = (value >> 4) & 0x0F
    low = value & 0x0F
    return high * 10 + low

# 示例
data = bytes([0x00, 0x0F, 0x42, 0x40])
print(parse_float32_be(data))  # 输出:1000.0

bcd_value = 0x12
print(parse_bcd(bcd_value))    # 输出:12

7. 物理接口详解与接线规范

7.1 RS-232与RS-485接口对比

Modbus协议本身是应用层协议,不依赖任何特定物理层,可在RS-232、RS-485、RS-422、以太网、光纤、蓝牙等多种介质上运行。工业现场最常用的是RS-485,以下是两种主要接口的详细对比:

7.1.1 RS-232接口

RS-232是早期最常见的串行通信标准,适合短距离点对点通信。

电平标准

逻辑状态 电压范围 说明
逻辑0(Space) +3V ~ +15V 数据线为正电压
逻辑1(Mark) -3V ~ -15V 数据线为负电压
过渡区 -3V ~ +3V 不确定区域,避免工作在此区

主要特性

特性 参数
通信方式 全双工(TX、RX独立)
最大传输距离 约15米(低速)/ 标准规定
最大波特率 理论115200bps
拓扑结构 点对点,一对一
抗干扰性 弱(单端信号)
常见接口 DB9、DB25

7.1.2 RS-485接口

RS-485采用差分信号传输,抗干扰性强,是工业现场首选接口。

电平标准

逻辑状态 A-B压差 说明
逻辑1 +2V ~ +6V A线高于B线
逻辑0 -2V ~ -6V A线低于B线

💡 差分信号原理:通过比较两根线的电压差来判断逻辑,共模噪声同时影响两根线,相减后被抵消,因此抗干扰极强。

主要特性

特性 参数
通信方式 半双工(同一时刻只能发或收)
最大传输距离 1200米(低波特率)
最大波特率 10Mbps(短距离)/ 实际工程中常用≤115200bps
最大节点数 标准32个驱动器,可扩展至247个(Modbus限制)
抗干扰性 强(差分信号)
常见接口 接线端子(A/B两线)

7.1.3 RS-232 vs RS-485 对比总结

对比项 RS-232 RS-485
信号类型 单端信号 差分信号
通信方式 全双工 半双工
传输距离 ≤15米 ≤1200米
节点数 1对1 1对247
抗干扰性
工业适用性 较差 优秀
Modbus适用 近距离调试 现场控制首选

7.2 RS-485接线规范

7.2.1 接线方式

复制代码
标准RS-485接线:
┌──────────┐                    ┌──────────┐
│   主站   │ ─── A ─────────── │   从站   │
│          │ ─── B ─────────── │          │
└──────────┘                    └──────────┘

BECKHOFF EL6021模块接线:
- 1/2脚短接 → 传感器A脚
- 5/6脚短接 → 传感器B脚
- 必须启用半双工模式,否则收发冲突

7.2.2 终端电阻设置

通讯距离 终端电阻 说明
≤10米 可不加 短距离通信稳定
>10米 必须 两端各加120Ω终端电阻
>100米 推荐 使用屏蔽双绞线

7.3 通信参数设置

7.3.1 串口参数

参数 常用值 说明
波特率 9600/19200/38400/115200 通信速率
数据位 8 通常为8位
停止位 1或2 通常为1位
校验位 无/奇/偶 常用无校验
数据格式 8-N-1 8数据位,无校验,1停止位

7.3.2 通信超时设置

参数 推荐值 说明
帧间隔 ≥3.5字符时间 T3.5静默时间
响应超时 100-1000ms 根据设备响应速度调整
重试次数 3次 通信失败时重试

7.4 批量操作限制

操作类型 最大数量 说明
读取保持寄存器 125个 单次请求最大数量
读取线圈 2000个 单次请求最大数量
写入保持寄存器 123个 字节数≤246
写入线圈 1968个 单次请求最大数量

8.Modbus实战案例

8.1 案例1:PLC控制变频器完整流程

场景:西门子S7-1200 PLC通过Modbus RTU控制V20变频器

硬件配置

  • S7-1200 PLC
  • V20变频器
  • RS485通信线(A-A,B-B连接)

变频器参数设置

参数 说明
P0700 5 Modbus控制
P1000 5 Modbus频率给定

核心报文交互

操作 请求帧 说明
启动变频器 02 06 00 63 00 01 6D F4 写入控制字40100=1
设定频率50Hz 02 06 00 64 13 88 2D F4 写入频率40101=5000
读取实际频率 02 04 00 00 00 01 90 0A 读取输入寄存器30001
响应帧 02 04 02 13 88 AD F3 实际频率=50.0Hz

8.2 案例2:读取电力仪表数据

场景:读取MMP-5000D电力仪表的有功电度

设备参数

  • 从站地址:1
  • 数据地址:保持寄存器40001-40002
  • 数据格式:32位IEEE754浮点数

实现代码(Python)

python 复制代码
from pymodbus.client import ModbusSerialClient

# 配置串口
client = ModbusSerialClient(
    port='/dev/ttyUSB0',
    baudrate=9600,
    bytesize=8,
    parity='N',
    stopbits=1
)

client.connect()

# 读取两个寄存器
result = client.read_holding_registers(
    address=0,      # 起始地址40001
    count=2,        # 读取2个寄存器
    slave=1         # 从站地址
)

if not result.isError():
    # 解析32位浮点数
    import struct
    data = result.registers
    # 大端序重组
    byte_data = bytes([
        data[0] >> 8, data[0] & 0xFF,
        data[1] >> 8, data[1] & 0xFF
    ])
    energy = struct.unpack('>f', byte_data)[0]
    print(f"有功电度: {energy} kWh")

client.close()

8.3 案例3:批量控制照明回路

场景:楼宇自控系统控制8个照明回路

实现代码(Python)

python 复制代码
from pymodbus.client import ModbusSerialClient

client = ModbusSerialClient(
    port='/dev/ttyUSB0',
    baudrate=9600,
    bytesize=8,
    parity='N',
    stopbits=1
)

client.connect()

# 控制模式:0101 0101(间隔开灯)
pattern = 0b01010101  # 0x55

# 写入多个线圈
result = client.write_coils(
    address=0,           # 起始地址00001
    values=[bool(pattern & (1 << i)) for i in range(8)],
    slave=1
)

if not result.isError():
    print("控制成功:线圈1/3/5/7=ON,线圈2/4/6/8=OFF")

client.close()

8.4 案例4:PLC程序实现(SCL语言)

场景:西门子S7-1200 PLC作为主站

scl 复制代码
// 定义变量
VAR
    MB_MASTER : MODBUS_COM;       // Modbus主站功能块
    REQ       : BOOL;              // 请求触发
    DONE      : BOOL;              // 完成标志
    ERROR     : BOOL;              // 错误标志
    STATUS    : WORD;              // 状态码
    REG_DATA  : ARRAY[0..9] OF WORD;  // 数据缓冲区
END_VAR

// 读取保持寄存器
MB_MASTER(
    REQ     := REQ,               // 触发请求
    UNIT    := 1,                 // 从站地址
    MODE    := 0,                 // 模式:0=RTU
    FUNC    := 3,                 // 功能码:03
    ADDR    := 0,                 // 起始地址40001
    NB      := 10,                // 读取10个寄存器
    DATA    := REG_DATA,          // 数据缓冲区
    DONE    => DONE,
    ERROR   => ERROR,
    STATUS  => STATUS
);

// 数据处理
IF DONE AND NOT ERROR THEN
    // 读取成功
    温度值 := REG_DATA[0] / 10.0;     // 转换为实际温度
    压力值 := REG_DATA[1] / 100.0;    // 转换为实际压力
    频率值 := REG_DATA[2] / 100.0;    // 转换为实际频率
END_IF;

9. 调试技巧与工具推荐

9.1 调试排查顺序

复制代码
调试流程:
┌─────────────────────────────────────────────────┐
│  1. 硬件层(接线/地址)                           │
│     ↓                                            │
│  2. 协议层(功能码/报文)                         │
│     ↓                                            │
│  3. 数据层(解析/量程)                           │
└─────────────────────────────────────────────────┘

9.2 常见问题排查

9.2.1 通信超时

现象:主站发送请求后,等待响应超时。

排查步骤

1. 检查物理连接(RS-485接线A-A、B-B)

2. 检查波特率、数据位、停止位等参数

3. 检查从站地址是否正确

4. 使用示波器检查信号波形

9.2.2 校验错误

现象:接收到响应但CRC校验失败。

可能原因

1. 波特率不匹配

2. 线路干扰严重

3. 校验算法实现错误

9.2.3 异常响应

*   现象:从站返回异常响应。

排查方法

异常码01:核对设备支持的功能码列表

异常码02:确认寄存器地址范围

异常码03:检查数值是否超出设备量程

9.3 调试工具推荐

9.3.1 PC端工具

工具名称 功能 特点 授权
Modbus Poll Modbus主站模拟器 图形化界面,支持RTU/TCP,数据可视化 付费(有试用)
Modbus Slave Modbus从站模拟器 可设置寄存器数据,配合Poll调试 付费(有试用)
QModMaster 开源主站工具 跨平台,完全免费,功能完整 开源免费
ModScan Modbus扫描工具 支持多功能码测试,自动扫描地址 商用
Serial Port Monitor 串口抓包分析 自动校验CRC,支持时序分析 付费
VSPD (Virtual Serial Port Driver) 虚拟串口工具 创建虚拟串口对,用于软件间联调 付费(有免费版)
Wireshark 网络抓包分析 支持Modbus TCP协议解析,完全免费 开源免费

9.3.2 Wireshark抓包分析Modbus TCP

Wireshark是分析Modbus TCP问题的利器,使用步骤:

1. 过滤Modbus TCP流量

复制代码
# 在Wireshark过滤栏输入:
modbus
# 或指定端口502:
tcp.port == 502

2. 报文解析示例

Wireshark会自动解析Modbus报文,显示:

复制代码
Frame 1: 12 bytes
  Modbus/TCP
    Transaction Identifier: 1
    Protocol Identifier: 0
    Length: 6
    Unit Identifier: 1
    Modbus
      Function Code: Read Holding Registers (3)
      Reference Number: 0
      Word Count: 2

3. 常见问题排查场景

  • 检查主站是否正确发出请求
  • 验证从站是否有响应
  • 分析响应时间(延迟问题)
  • 检测报文格式是否符合规范

9.3.3 在线工具

工具名称 链接 功能
Modbus在线工具 https://modbuskit.com/zh/modbus-rtu 在线报文解析、CRC计算
CRC计算器 在线搜索 校验码计算

9.3.4 手机端工具

  • Modbus调试助手(安卓)
  • 支持多设备实时监测
  • 便携式现场调试

10.Modbus协议进阶

10.1 Modbus网关

Modbus网关用于实现不同协议之间的转换:

复制代码
┌─────────────┐          ┌─────────────┐          ┌─────────────┐
│ Modbus RTU  │  ──────> │ Modbus网关   │  ──────> │ Modbus TCP  │
│ 从站设备    │          │ (协议转换)   │          │ 主站系统    │
└─────────────┘          └─────────────┘          └─────────────┘
     RS-485                  双接口                   以太网

网关功能

  • 协议转换:RTU ↔ TCP
  • 地址映射:虚拟地址映射
  • 数据缓存:提高响应速度
  • 并发处理:多客户端支持

10.2 Modbus安全

10.2.1 安全风险分析

Modbus协议设计于1979年,当时工业网络是物理隔离的封闭系统,因此原始协议不包含任何安全机制。随着工业设备接入互联网,这成为了重大安全隐患:

风险类型 说明 危害等级
无认证机制 任何人只要能连上网络就能读写数据 🔴 高危
无加密传输 数据明文传输,可被嗅探 🔴 高危
无访问控制 无法区分合法/非法主站 🟠 中危
地址可枚举 设备地址(1-247)可被暴力扫描 🟠 中危
无时间戳 无法检测重放攻击 🟡 低危

10.2.2 安全防护措施

物理层安全

  • RS-485总线物理隔离,限制物理接入
  • 禁止将Modbus RTU设备直接暴露到互联网

网络层安全(针对Modbus TCP)

复制代码
安全架构示例:
┌────────────┐    防火墙    ┌────────────┐    VLAN隔离    ┌────────────┐
│  外部网络  │ ──────────> │  DMZ区域   │ ─────────────> │ 工业控制网 │
│ (Internet) │             │ (安全网关)  │               │  Modbus设备│
└────────────┘             └────────────┘               └────────────┘
  • 防火墙规则:仅允许已知IP访问TCP端口502
  • VLAN隔离:将工业控制网络与办公网络隔离
  • VPN隧道:远程访问必须经过VPN加密通道
  • Modbus安全网关:部署支持白名单过滤的专用网关

10.2.3 Modbus Secure(下一代安全方案)

Modbus Secure 是基于 IEC 62351 标准的安全扩展,在原有Modbus基础上增加了TLS加密层:

特性 标准Modbus TCP Modbus Secure
传输层 明文TCP TLS 1.2+
认证 证书认证
加密 AES-256
端口 502 802
标准 Modbus Organization IEC 62351-8

💡 实际应用建议 :Modbus Secure目前部署较少,对于新建系统且安全要求高的场景,可考虑直接采用 OPC UA 协议(内置安全机制更完善)。

10.3 协议演进:Modbus vs OPC UA

随着工业4.0的发展,OPC UA逐渐被推荐为Modbus的替代方案。了解二者的区别有助于做出合适的技术选型:

对比项 Modbus RTU/TCP OPC UA
发布年份 1979 2006
架构 主从(Master/Slave) 客户端/服务器 + 发布/订阅
数据模型 寄存器/线圈(扁平) 面向对象,有节点/类型系统
安全性 无内建安全 内建认证+加密
互操作性 基本 强(包含语义信息)
实现复杂度 简单 复杂
资源消耗 极低 较高
适用场景 传统设备、嵌入式 新建系统、跨厂商集成

技术选型建议

场景 推荐协议
传统PLC、变频器、仪表通信 Modbus RTU/TCP
新建IoT系统(云端集成) Modbus TCP + MQTT网关
高安全要求的关键系统 OPC UA 或 Modbus Secure
跨厂商设备互联互通 OPC UA

10.4 Modbus与物联网(IoT)集成

10.4.1 Modbus over MQTT架构

随着IoT的发展,Modbus over MQTT 成为将传统工业设备接入云平台的主流方案:

复制代码
现场层                 边缘层                    云平台层
┌──────────┐      ┌─────────────────┐      ┌──────────────┐
│ PLC设备  │      │                 │      │              │
│ 变频器   │─RS485─│  IoT边缘网关    │─MQTT─│  云平台      │
│ 传感器   │      │  (Modbus→MQTT)  │      │  (数据存储/  │
│ 仪表     │      │  边缘计算/过滤   │      │   可视化)    │
└──────────┘      └─────────────────┘      └──────────────┘
  Modbus RTU/TCP      协议转换 + 数据预处理    MQTT/HTTP/WebSocket

数据流向

  1. 边缘网关按配置周期(如1s)轮询Modbus从站
  2. 将采集到的数据转换为JSON/CBOR格式
  3. 通过MQTT Publish发布到云平台Topic
  4. 云平台订阅Topic,实现实时监控与数据存储

10.4.2 常见IoT网关方案

方案 说明 适用场景
Node-RED 可视化流编程,内置Modbus节点 快速原型、小规模部署
Ignition 工业级SCADA平台,原生支持 大型工厂MES/SCADA
自研网关 基于Python/C++/Node.js开发 定制化需求
商用网关 研华、摩莎等厂商硬件网关 免维护、可靠性高

10.4.3 Node.js实现示例

javascript 复制代码
// node-modbus-serial + MQTT 实现数据采集上报
const ModbusRTU = require("modbus-serial");
const mqtt = require("mqtt");

const modbusClient = new ModbusRTU();
const mqttClient = mqtt.connect("mqtt://broker.example.com");

// 连接Modbus RTU设备
modbusClient.connectRTUBuffered("/dev/ttyUSB0", { baudRate: 9600 });
modbusClient.setID(1); // 从站地址

// 定时采集数据
setInterval(async () => {
    try {
        // 读取保持寄存器 40001-40002(2个)
        const data = await modbusClient.readHoldingRegisters(0, 2);
        
        const payload = {
            timestamp: Date.now(),
            registers: data.data,
            temperature: data.data[0] / 10.0,  // 寄存器值/10=实际温度
            pressure: data.data[1] / 100.0      // 寄存器值/100=实际压力
        };
        
        // 发布到MQTT
        mqttClient.publish("factory/device01/data", JSON.stringify(payload));
        console.log("数据上报成功:", payload);
    } catch (err) {
        console.error("采集失败:", err);
    }
}, 1000); // 每秒采集一次

10.4.4 Python pymodbus完整示例

python 复制代码
from pymodbus.client import ModbusTcpClient
import json
import time

def read_device_data(client, slave_id=1):
    """读取设备数据并返回字典"""
    result = {}
    
    # 读取保持寄存器(温度、压力、流量)
    hr = client.read_holding_registers(address=0, count=3, slave=slave_id)
    if not hr.isError():
        result['temperature'] = hr.registers[0] / 10.0   # 实际温度
        result['pressure'] = hr.registers[1] / 100.0     # 实际压力
        result['flow'] = hr.registers[2] / 10.0          # 实际流量
    
    # 读取线圈状态(设备运行状态)
    coils = client.read_coils(address=0, count=4, slave=slave_id)
    if not coils.isError():
        result['pump_running'] = coils.bits[0]
        result['valve_open'] = coils.bits[1]
        result['alarm'] = coils.bits[2]
        result['fault'] = coils.bits[3]
    
    return result

# 连接Modbus TCP设备
client = ModbusTcpClient(host='192.168.1.10', port=502)
client.connect()

try:
    while True:
        data = read_device_data(client, slave_id=1)
        print(f"采集时间: {time.strftime('%H:%M:%S')}")
        print(f"温度: {data.get('temperature', 'N/A')} °C")
        print(f"压力: {data.get('pressure', 'N/A')} MPa")
        print(f"泵状态: {'运行' if data.get('pump_running') else '停止'}")
        print(f"报警: {'有报警' if data.get('alarm') else '正常'}")
        print("-" * 40)
        time.sleep(1)
finally:
    client.close()

11.学习资源汇总

11.1 官方规范文档

文档名称 说明
Modbus协议规范 官方协议文档
Modbus应用协议 V1.1b3 协议实现指南
Modbus TCP规范 TCP实现规范

11.2 优质教程链接

11.2.1 基础教程

教程名称 链接 说明
Modbus通讯协议解析 https://www.cnblogs.com/altairshmily/articles/19038177 详细的协议解析
Modbus RTU基础教程 https://modbuskit.com/zh/blog/modbus-rtu-basic-tutorial 从零开始学习
Modbus协议详解 https://www.modbus.cn/modbus-guide 官方教程
Modbus RTU功能码终极指南 https://mp.weixin.qq.com/s/g3xXqHsgF9WPPusHCgboPA 实战案例丰富

11.2.2 进阶教程

教程名称 链接 说明
RTU/TCP/ASCII对比 https://modbuskit.com/zh/blog/modbus-rtu-tcp-ascii-comprehensive-comparison 三种模式详解
功能码详解 https://www.xlink.cn/docs/platform/protocol-description/Modbus/code/ 功能码说明
CRC校验算法 https://blog.csdn.net/m0_47673526/article/details/134290383 校验原理

11.2.3 实战教程

教程名称 链接 说明
PLC Modbus通信 https://zhuanlan.zhihu.com/p/356388866 西门子PLC实例
Python Modbus https://blog.csdn.net/Dontla/article/details/134413891 Python实现

11.3 在线工具

工具名称 链接 功能
Modbus在线工具 https://modbuskit.com/zh/modbus-rtu 报文解析、CRC计算
Modbus功能码查询 https://www.xlink.cn/docs/platform/protocol-description/Modbus/code/ 功能码说明
Modbus地址对照表 https://www.xlink.cn/docs/platform/protocol-description/Modbus/address/ 地址说明

11.4 开发库推荐

11.4.1 Python库

库名称 说明 安装命令
pymodbus 功能最全 pip install pymodbus
minimalmodbus 简单易用 pip install minimalmodbus
modbus-tk 轻量级 pip install modbus_tk

11.4.2 C/C++库

库名称 说明
libmodbus 开源C库,跨平台
QModbus Qt框架实现

12.常见问题解答(FAQ)

Q1:Modbus RTU和TCP有什么区别?

A:主要区别在于传输方式和物理介质:

  • RTU使用串行通信(RS-485),传输距离有限,单网络节点数有限
  • TCP使用以太网,传输距离无限制,支持更多节点和并发连接

Q2:如何选择合适的波特率?

A:波特率选择需要考虑:

  • 通信距离:距离越远,波特率应越低
  • 环境干扰:干扰大时应降低波特率
  • 数据量:数据量大时可提高波特率
  • 推荐范围:9600-115200bps

Q3:Modbus通信失败怎么办?

A:按以下步骤排查:

  1. 检查物理连接(接线、终端电阻)
  2. 检查通信参数(波特率、数据位等)
  3. 检查从站地址
  4. 使用调试工具抓包分析
  5. 检查CRC校验

Q4:如何处理浮点数数据?

A:Modbus寄存器为16位整数,处理浮点数方法:

  • 使用两个连续寄存器存储一个32位浮点数
  • 注意字节序(大端/小端)
  • 使用IEEE 754标准解析

Q5:地址映射怎么计算?

A

  • 逻辑地址40001 → 协议地址0(40001-40001=0)
  • 逻辑地址40100 → 协议地址99(40100-40001=99)
  • 线圈00265 → 协议地址264(265-1=264)

13. 核心总结

13.1 三角逻辑

Modbus RTU的核心是**「功能码-寄存器-报文」三角逻辑**:

复制代码
        ┌─────────────┐
        │   功能码    │
        │ (定义操作)  │
        └──────┬──────┘
               │
       ┌───────┴───────┐
       ▼               ▼
┌─────────────┐ ┌─────────────┐
│   寄存器    │ │    报文     │
│ (存储数据)  │ │ (实现传输)  │
└─────────────┘ └─────────────┘

13.2 调试排查顺序

复制代码
&emsp;&emsp; 先硬件(接线/地址)→ 再协议(功能码/报文)→ 后数据(解析/量程)

13.3 关键记忆点

要点 说明
功能码 定义操作类型,异常时+0x80
地址 逻辑地址-1=协议地址
CRC 低字节在前,可用在线工具计算
数据 注意字节序和数据类型
超时 建议100-1000ms

13.4 工程师实战经验总结

以下是来自10年工业通信工程师积累的宝贵经验,帮助你少走弯路:

13.4.1 为什么Modbus经久不衰?

简单 > 完美

在工业现场,可靠性、低成本、易实现比功能丰富更重要。Modbus用40多年证明了这一点。

Modbus最大的价值在于三点:

  1. 免费:无专利授权费用,大量使用获得了广泛生态支持
  2. 简单:协议帧格式紧凑,初学者1天就能理解完整协议
  3. 兼容:无论多老的设备,几乎都支持Modbus------这正是它的护城河

13.4.2 学习路径建议

复制代码
阶段1(理解):
  掌握四种存储区 → 理解功能码含义 → 读懂RTU帧格式

阶段2(实践):
  安装ModbusPoll/ModbusSlave → 搭建虚拟串口调试环境
  → 自己组帧发报文 → 验证响应

阶段3(项目):
  接实际设备(PLC/变频器/仪表)→ 读设备手册找寄存器地址
  → 用Python/C#编写采集程序 → 处理异常和超时

阶段4(进阶):
  学习Modbus TCP → 了解Modbus网关 → 研究IoT集成方案

13.4.3 常见"坑"整理

坑点 说明 解决方案
地址偏移 设备手册写40001,实际发报文要用0 协议地址 = 逻辑地址 - 1(或-区起始值)
字节序混乱 32位浮点数高低字节顺序不一致 查设备手册,不同厂商字节序可能不同
CRC字节序 CRC低字节先发,高字节后发 注意:CRC低位在前,与多数想象相反
地址冲突 总线上两个设备地址相同导致通信混乱 通电前用调试工具逐个检查设备地址
终端电阻遗漏 长距离不加120Ω终端电阻,信号反射 超过10米必须两端各加120Ω
波特率不一致 主从波特率设置不同,无响应 统一用9600波特率,稳定后再提速
广播地址误用 发给地址0,所有从站执行但无人响应 广播写有效,但不要用广播读
响应超时太短 某些老设备响应慢,超时设太短导致误判 建议初调时超时设置1000ms

13.4.4 主流设备Modbus接入要点

设备类型 典型品牌 关键要点
PLC 西门子、汇川、信捷、台达 国产PLC基本都支持Modbus,查手册找从站地址配置参数
变频器 西门子V20、ABB、汇川 需设置Modbus控制模式(P0700=5等),频率/控制字有固定寄存器
电力仪表 安科瑞、华立、科陆 数据多为32位浮点,需要两个寄存器,注意字节序
传感器 气体、温湿度、压力 通常输出保持寄存器,分辨率和量程需对照手册转换
HMI 威纶通、信捷 HMI可作主站也可作从站,注意配置Modbus驱动

14.附录

附录A:功能码速查表

功能码 名称 操作对象 读写
01 读线圈 线圈
02 读离散输入 离散输入
03 读保持寄存器 保持寄存器
04 读输入寄存器 输入寄存器
05 写单个线圈 线圈
06 写单个寄存器 保持寄存器
0F (15) 写多个线圈 线圈
10 (16) 写多个寄存器 保持寄存器
15 (21) 强制多点线圈 线圈
16 (22) 预置单个寄存器 保持寄存器

附录B:异常码速查表

异常码 名称 说明
01 ILLEGAL FUNCTION 非法功能码
02 ILLEGAL DATA ADDRESS 非法地址
03 ILLEGAL DATA VALUE 非法数据值
04 SLAVE DEVICE FAILURE 从站故障
05 ACKNOWLEDGE 确认/忙
06 SLAVE DEVICE BUSY 拒绝执行

附录C:常用波特率参数

波特率 T3.5时间 最大距离(RS-485)
1200 29.17ms 1200m
2400 14.58ms 1000m
4800 7.29ms 800m
9600 3.65ms 600m
19200 1.82ms 400m
38400 0.91ms 200m
115200 0.30ms 50m

附录D:数据类型对照表

数据类型 地址前缀 位数 读写 功能码
线圈 0xxxx 1位 R/W 01,05,15
离散输入 1xxxx 1位 R 02
输入寄存器 3xxxx 16位 R 04
保持寄存器 4xxxx 16位 R/W 03,06,16

15.参考资料

  1. Modbus协议官方规范(Modbus Organization)
  2. 《Modbus通讯协议完全指南》
  3. Modbus通讯协议解析
  4. 在线Modbus工具
  5. Modbus RTU基础教程
  6. Modbus功能码详解
  7. Modbus地址模型
  8. 三种模式对比
  9. CRC校验算法
  10. PLC Modbus通信实例
  11. Modbus RTU功能码终极指南
  12. 10年工程师的MODBUS总结(新阁教育付工)
  13. 详解Modbus通信协议(ARM研习社)
  14. Modbus协议详解(王小工)
  15. IEC 62351 工业通信安全标准
相关推荐
麦德泽特1 天前
基于 Go 语言的 Modbus 项目实战:构建高性能、可扩展的工业通信服务器
服务器·开发语言·golang·modbus·rtu
李庆政3709 天前
modbus协议四 rtu Over tcp & mbslave & CRC校验码计算方法
网络协议·tcp/ip·modbus·rtu over tcp
疆鸿智能研发小助手14 天前
PROFINET转MODBUS TCP网关接安科瑞马达保护器案例
modbus·马达保护器·工业自动化·profinet·工业通讯·协议转换网关
疆鸿智能研发小助手15 天前
破局机床精密加工:疆鸿智能PROFINET转MODBUS TCP通讯壁垒终结者
modbus·modbus tcp·工业自动化·profinet·工业通讯·协议转换网关·智控机床
謓泽17 天前
【MODBUS】串口 RTU / Modbus TCP / 透明就绪
网络·串口·modbus
疆鸿智能研发小助手20 天前
疆鸿智能ETHERNET IP转MODBUS,让施耐德变频器“对话”无界
modbus·工业自动化·变频器·ethernet ip·工业通讯·协议转换网关
czhc11400756631 个月前
Modbus wpf 35
modbus
Hello_Embed2 个月前
Modbus 传感器开发:从寄存器规划到点表设计
笔记·stm32·单片机·学习·modbus
Hello_Embed2 个月前
Modbus 传感器开发:STM32F030 libmodbus 移植
笔记·stm32·学习·freertos·modbus