总体上来说,发送命令格式如下:模块号(1字节)功能码 起始地址(2字节) 数据(X字节) CRC(2位)
模块号(1字节)功能码(要读取的寄存器,读 01,设置05,Coil Status/Input Status,1字节)起始地址(2字节,H->L) 数据 CRC校验
响应格式
模块号(1字节)功能码(1字节)字节数 数据(H->L) CRC校验
功能码:01 读取线圈状态,取得一组逻辑线圈的当前状态(ON/OFF)
模块号(1字节)功能码 起始地址(2字节,H->L) 读取位数(2字节) CRC(2位)
响应格式:
模块号(1字节)功能码(1字节)字节数 数据(L) 数据(H) CRC校验
02 01 00 10 00 08 3C 3A
返回(DO-0亮)
02 01 01 01 90 0C
返回(DO-0 不亮)
02 01 01 00 51 CC
获得00开始,连续16个通道的值
02,01,00,00,00,10,3D,F5
返回(只有DI-1有输入值)
02,01,02,02,00,FC,9C
Device ID:2,Address:0001,Length:8
02 01 00 00 00 08 3D FF
02 01 01 00 51 CC
02 01 01 02 51 CC(DI 1 亮的时候的返回)
Device ID:2,Address:0002,Length:8
02 01 00 01 00 08 6C 3F
02 01 01 01 90 0C
功能码:05 强制单路开出,给PLC写数据
注:写0xFF00表示ON,写0x0000表示OFF,线圈地址比地址少小1
注:返回帧与主站相同
模块号(1字节)功能码 起始地址(2字节) 数据(2字节) CRC(2位)
响应格式:返回帧与主站相同
设置DO-0(地址17)亮
02 05 00 10 FF 00 8D CC
返回
02 05 00 10 FF 00 8D CC
设置DO-0 不亮
02 05 00 10 00 00 CC 3C
返回
02 05 00 10 00 00 CC 3C
同时设置多个输出格式(功能码:15)
模块号(1字节)功能码(15)起始地址(2字节) 设置个数(2字节)数据的字节数(1字节)数据(H->L) CRC校验
响应格式
模块号(1字节)功能码(1字节)起始地址(2字节) 设置个数(2字节) CRC校验
eg:
(设置17站开始的8个站,为输出):
02,0F,00,10,00,08,01,FF,3F,03
响应:
02,0F,00,10,00,08,55,FB
(设置17站开始的8个站,全灭):
00,10,00,08,01,00,7F,43
响应:
02,0F,00,10,00,08,55,FB
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
以下是更详细的说明,摘自http://blog.sina.com.cn/s/blog_5d4d58a90100rkxf.html
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
基于串口的Modbus软件开发
----------------------八股--------------------
1、已经有好多个测试软件了,但是没有开源。如modbusscan 7.0(ModScan32.exe),Modbus Poll version, 4.3.1, Modbustester.exe,ModLink,ModbusSimulator
2、协议比较老了,但是很管用。
主从方式,主站初始化传输。从站根据主设备查询提供的数据作出反应。
主站查询格式:站(或广播)地址、功能代码、要发送的数据、错误检测域。
从站回应格式:确认要行动的域、返回的数据、错误检测域。如果在消息接收过程中发生错误,或从站不能执行其命令,从站将建立错误消息并把它作为回应发送出去。
从站地址是0...247,0为广播地址
ASCII模式
: |
地址 |
功能代码 |
数据数量 |
数据1 |
... |
数据n |
LRC高字节 |
LRC低字节 |
回车 |
换行 |
代码系统:
十六进制,ASCII字符0...9,A...F
消息中的每个ASCII字符都是一个十六进制字符组成
每个字节的位:
1个起始位
7个数据位,最小的有效位先发送
1个奇偶校验位,无校验则无
1个停止位(有校验时),2个Bit(无校验时)
错误检测域
LRC(纵向冗长检测)
使用ASCII模式,消息以冒号(:)字符(ASCII码 3AH)开始,以回车换行符结束(ASCII码 0DH,0AH)。
其它域可以使用的传输字符是十六进制的0...9,A...F。网络上的设备不断侦测“:”字符,当有一个冒号接收到时,每个设备都解码下个域(地址域)来判断是否发给自己的。
消息中字符间发送的时间间隔最长不能超过1秒,否则接收的设备将认为传输错误。
RTU模式
地址 |
功能代码 |
数据数量 |
数据1 |
... |
数据n |
CRC低字节 |
CRC高字节 |
|
|
代码系统:
8位二进制,十六进制数0...9,A...F
消息中的每个8位域都是一个两个十六进制字符组成
每个字节的位:
1个起始位
8个数据位,最小的有效位先发送
1个奇偶校验位,无校验则无
1个停止位(有校验时),2个Bit(无校验时)
错误检测域
CRC(循环冗长检测)
深入理解ModBus功能码
Modbus主要功能码
功能码 |
名称 |
注释 |
|
01 |
Read Coil Status |
(线圈状态0x)(读PLC的开出状态) |
bit 读位 |
02 |
Read Input Status |
(输入状态1x) (读PLC的开入状态) |
bit 读位,只读DI |
03 |
Read Holding Register |
(保持寄存器4x HR) (读模出状态) |
读整形、状态字、浮点型、字符型,与16对应 |
04 |
Read Input Register |
(输入寄存器3x AR) (读PLC模入状态) |
读整形、状态字、浮点型 只读AI |
05 |
Write Single Coil |
(强制单路开出,给PLC写数据) |
写单个位 |
06 |
Write Single Register |
(强制单路模出,给PLC写数据) |
写单个整形、状态字、浮点型、字符型,写HR4x的地址区 |
15 |
Write Multiple Coil |
(强制多路开出,给PLC写数据) |
写多个位 |
16(0x10H) |
Write Multiple Register |
(强制多路模出,给PLC写数据) |
写多个整形、状态字、浮点型、字符型 |
15和16可能是用于一次写一串数据的,不允许单个写。如时间的世纪、年、月、日、时、分、秒要一次写下去
设备和Modbus 地址范围对应表 |
||||
设备地址 |
Modbus地址 |
描述 |
功能 |
R/W |
1...10000* |
address - 1 |
Coils (outputs) |
0 |
Read/Write |
10001...20000* |
address - 10001 |
Discrete Inputs |
01 |
Read |
40001...50000* |
address - 40001 |
Holding Registers |
03 |
Read/Write |
30001...40000* |
address - 30001 |
Input Registers |
04 |
Read |
*最大值与设备相关
注:设备地址是从1开始的,所以Modbus写入地址要在设备地址上减1。
协议格式
功能码:01 (线圈状态0x)(读开出状态)
从站地址 |
功能码 |
H 地址 |
L 地址 |
H Coils |
L Coils |
CRC |
|
04 |
01 |
00 |
0A |
00 |
0D |
DD |
98 |
主站
从站地址 |
功能码 |
字节数 |
Coils 7..10 |
Coils 27..20 |
CRC |
|
|
01 |
02 |
0A |
11 |
50 |
B3 |
从站
功能码:02 (输入状态1x) (读开入状态)
从站地址 |
功能码 |
H 地址 |
L 地址 |
H Input |
L Input |
CRC |
|
04 |
02 |
00 |
0A |
00 |
0D |
99 |
98 |
主站
从站地址 |
功能码 |
字节数 |
Input 7..10 |
Input 27..20 |
CRC |
|
|
02 |
02 |
0A |
11 |
14 |
B3 |
从站
功能码:03 (保持寄存器4x HR) (读模出状态)
从站地址 |
功能码 |
H 地址 |
L 地址 |
H 数据 |
L 数据 |
CRC |
|
01 |
03 |
00 |
00 |
00 |
02 |
C4 |
0B |
主站
从站地址 |
功能码 |
字节数 |
H数据 |
L数据 |
H数据 |
L数据 |
CRC |
|
|
03 |
04 |
00 |
06 |
00 |
05 |
DA |
31 |
从站
功能码:04 (输入寄存器3x AR) (读模入状态)
从站地址 |
功能码 |
H 地址 |
L 地址 |
H 数据 |
L 数据 |
CRC |
|
01 |
04 |
00 |
00 |
00 |
02 |
71 |
CB |
主站
从站地址 |
功能码 |
字节数 |
H数据 |
L数据 |
H数据 |
L数据 |
CRC |
|
|
04 |
04 |
00 |
06 |
00 |
05 |
DB |
86 |
从站
功能码:05 强制单路开出,给PLC写数据
从站地址 |
功能码 |
H 地址 |
L 地址 |
H 数据 |
L 数据 |
CRC |
|
11 |
05 |
00 |
AC |
FF |
00 |
4E |
8B |
主站
注:写0xFF00表示ON,写0x0000表示OFF
从站地址 |
功能码 |
H 地址 |
L 地址 |
H数据 |
L数据 |
CRC |
|
|
05 |
00 |
AC |
FF |
00 |
4E |
8B |
从站
注:返回帧与主站相同
功能码:06 强制单路模出,给PLC写数据
从站地址 |
功能码 |
H 地址 |
L 地址 |
H 数据 |
L 数据 |
CRC |
|
11 |
06 |
00 |
01 |
00 |
03 |
9A |
9B |
主站
从站地址 |
功能码 |
H 地址 |
L 地址 |
H数据 |
L数据 |
CRC |
|
|
06 |
00 |
01 |
00 |
03 |
9A |
9B |
从站
注:返回帧与主站相同
功能码:15 强制多路开出,给PLC写数据
帧中位 |
1 |
1 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
位置 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
本例位置 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
- |
- |
- |
- |
- |
- |
29 |
28 |
从站地址 |
功能码 |
H 地址 |
L 地址 |
H线圈数量 |
L线圈数量 |
字节数 |
H数据 |
L数据 |
CRC |
|
11 |
0F |
00 |
13 |
00 |
0A |
02 |
CD |
01 |
BF |
0B |
主站
从站地址 |
功能码 |
H 地址 |
L 地址 |
H线圈数量 |
L线圈数量 |
CRC |
|
|
05 |
00 |
13 |
00 |
0A |
4E |
8B |
从站
注:变量写的从站反馈没有数据
功能码:16 强制多路模出到保持寄存器,给PLC写数据
例:给17站从40002HR开始的2个寄存器,数据为000A和0102,MB地址:40002-1=40001=MB1
从站地址 |
功能码 |
H地址 |
L地址 |
H模入数量 |
L模入数量 |
字节数 |
H |
L |
H |
L |
CRC |
|
11 |
10 |
00 |
01 |
00 |
02 |
04 |
00 |
0A |
01 |
02 |
C6 |
F0 |
主站
从站地址 |
功能码 |
H 地址 |
L 地址 |
H线圈数量 |
L线圈数量 |
CRC |
|
|
10 |
00 |
01 |
00 |
02 |
12 |
98 |
从站
注:变量写的从站反馈没有数据
秦批:Modbus的从站反馈数据没有数据帧的编号(不像TCP协议那样),所以不会显示这个反馈数据是主站的哪次请求的。若主站一次发送N个数据,从站而没有及时反馈,这样就乱了,主站就不知道从站发过来的数据是哪次命令的。
单变量与多变量:读可以读单读多,命令不分读几个。但是写要区分写一个还是写多个,所以写命令有写单线圈和写多线圈。
Modbus所有功能码
功能码 |
名称 |
作用 |
01 |
读取线圈状态 |
取得一组逻辑线圈的当前状态(ON/OFF) |
02 |
读取输入状态 |
取得一组开关输入的当前状态(ON/OFF) |
03 |
读取保持寄存器 |
在一个或多个保持寄存器中取得当前的二进制值 |
04 |
读取输入寄存器 |
在一个或多个输入寄存器中取得当前的二进制值 |
05 |
强置单线圈 |
强置一个逻辑线圈的通断状态 |
06 |
预置单寄存器 |
把具体二进值装入一个保持寄存器 |
07 |
读取异常状态 |
取得8个内部线圈的通断状态,这8个线圈的地址由控制器决定,用户逻辑可以将这些线圈定义,以说明从机状态,短报文适宜于迅速读取状态 |
08 |
回送诊断校验 |
把诊断校验报文送从机,以对通信处理进行评鉴 |
09 |
编程(只用于484) |
使主机模拟编程器作用,修改PC从机逻辑 |
10 |
控询(只用于484) |
可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码9的报文发送后,本功能码才发送 |
11 |
读取事件计数 |
可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其他应答产生通信错误时 |
12 |
读取通信事件记录 |
可是主机检索每台从机的ModBus事务处理通信事件记录。如果某项事务处理完成,记录会给出有关错误 |
13 |
编程(184/384 484 584) |
可使主机模拟编程器功能修改PC从机逻辑 |
14 |
探询(184/384 484 584) |
可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13的报文发送后,本功能码才得发送 |
15 |
强置多线圈 |
强置一串连续逻辑线圈的通断 |
16 |
预置多寄存器 |
把具体的二进制值装入一串连续的保持寄存器 |
17 |
报告从机标识 |
可使主机判断编址从机的类型及该从机运行指示灯的状态 |
18 |
(884和MICRO 84) |
可使主机模拟编程功能,修改PC状态逻辑 |
19 |
重置通信链路 |
发生非可修改错误后,是从机复位于已知状态,可重置顺序字节 |
20 |
读取通用参数(584L) |
显示扩展存储器文件中的数据信息 |
21 |
写入通用参数(584L) |
把通用参数写入扩展存储文件,或修改之 |
22~64 |
保留作扩展功能备用 |
|
65~72 |
保留以备用户功能所用 |
留作用户功能的扩展编码 |
73~119 |
非法功能 |
|
120~127 |
保留 |
留作内部作用 |
128~255 |
保留 |
用于异常应答 |
ModBus功能码与数据类型对应表
代码 |
功能 |
数据类型 |
01 |
读 |
位 |
02 |
读 |
位 |
03 |
读 |
整型、字符型、状态字、浮点型 |
04 |
读 |
整型、状态字、浮点型 |
05 |
写 |
位 |
06 |
写 |
整型、字符型、状态字、浮点型 |
08 |
N/A |
重复“回路反馈”信息 |
15 |
写 |
位 |
16 |
写 |
整型、字符型、状态字、浮点型 |
17 |
读 |
字符型 |
Modbus变量地址
映射地址 |
功能 |
地址类型 |
存取方式 |
描述 |
0xxxx |
01,05,15 |
Coil |
读写 |
|
1xxxx |
02 |
离散输入 |
只读 |
|
2xxxx |
03,04,06,16 |
浮点寄存器 |
读写 |
两个连续16位寄存器表示一个浮点数(ieee754格式32位) |
3xxxx |
04 |
输入寄存器 |
只读 |
每个寄存器表示一个16位无符号整数(0~65535)同上 |
4xxxx |
03,06,16 |
保持寄存器 |
读写 |
|
5xxxx |
03,04,06,16 |
ascii字符 |
读写 |
每个寄存器表示两个ascii字符 |
FAQ0:ABB组态软件有功能块读线圈-1,读线圈-8,读寄存器-1,读寄存器-8的原因是什么?
因为Modbus功能码的读操作可以指定数据长度。如果从站的地址连续,则可以用一次读八个变量的操作。如果不连续,可以一次读一个。读八个的效率要高一些。
FAQ1 :MB地址的问题:MB经常用30001,40001这样的地址,WHY?给个理由。
ANS1 :地址以3开头,如IFIX的mb1中地址用30001、3001、300001表示的是功能码4的操作,即读输入寄存器。同理, 地址以4开头表示的是功能码3的操作,即对指的是对输出寄存器/内部寄存器进行的操作。虽然Modbus有两个字节表示地址0xFFFF,共64K,但是一般用不了那么多的。
例:高安屯垃圾电厂DCS Modbus通讯点表
DCS-TAG_№ |
DESCRIPTION |
Type |
Data |
Addr. |
XA 0FG10-1 |
ACTIVATED CARBON STORAGE SILO VENTING FILTER FAN FAULT |
DI |
BOOL |
00407 |
HC #RC10-O |
No.# INLET DAMPER FOR NID SYSTEM A IN SERVICE COMMAND |
DO |
BOOL |
10001 |
ATO #FG03-S |
No.# SET POINT SO2 EMISSION |
AO |
INT |
30002 |
AI #RC01 |
No.# REACTOR INLET GAS SO2 ANALYZER |
AI |
INT |
40001 |
注:地址以2开头表示浮点数,以0,1,3,4开头表示整数。
FAQ2 : Modbus功能码的名称来源是什么?
ANS2: 因为Modbus主要用于与PLC通讯,所以Modbus的地址空间命名也服从PLC的方式。我一起在弄DCS,没玩过PLC。所以没明白这个道理之前,根本搞不明白Modbus的功能码要那么叫,经过两天多的研究研究研究,终于大彻大悟了。
功能码 |
名称 |
注释 |
|
01 |
Read Coil Status |
读PLC的开出状态,可能是内部量 |
可读可写 |
02 |
Read Input Status |
读PLC的开入DI点状态 |
只读DI,通道来 |
03 |
Read Holding Register |
读PLC内部模拟量 |
|
04 |
Read Input Register |
读PLC的模入AI状态 |
只读AI,通道来 |
05 |
Write Single Coil |
给PLC写开关量数据 |
写单个位 |
06 |
Write Single Register |
给PLC写模拟量数据 |
写模拟量 |
15 |
Write Multiple Coil |
给PLC写多个开关量数据 |
写多个位 |
16(0x10H) |
Write Multiple Register |
给PLC写多个模拟量数据 |
写多个模拟量 |