pip install modbus_tk
https://blog.csdn.net/qq_40423339/article/details/96289321
链接:https://pan.baidu.com/s/10C_3VL04Ycb5C_-YfrPj_w
提取码:jhuv
复制这段内容后打开百度网盘手机App,操作更方便哦
在通过TCP通信的时候我们需要下载modbusslave和modbuspol,但modbusslave有一个缺陷,就是最多只能连接100个寄存器或者线圈,我们可以通过使用nodbus-TCP client Tester来模拟更多的线圈或者寄存器,他最多支持65536个线圈或者寄存器。
打开slave,点击connection-connect,配置好参数选择OK,开始监听
简单修改设备id为1,function为03 Holding Register,点击ok
import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus_tcp as modbus_tcp
logger = modbus_tk.utils.create_logger("console")
if __name__ == "__main__":
try:
# 连接MODBUS TCP从机
master = modbus_tcp.TcpMaster(host="192.168.1.66")
master.set_timeout(5.0)
logger.info("connected")
# 读保持寄存器
demo1 = master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 9)
print(demo1)
# 读输入寄存器
logger.info(master.execute(3, cst.READ_INPUT_REGISTERS, 0, 9, output_value=1))
# 读线圈寄存器
logger.info(master.execute(2, cst.READ_COILS, 0, 8))
logger.info(master.execute(2, cst.WRITE_SINGLE_COIL, 1, output_value=2))
# 读离散输入寄存器
logger.info(master.execute(4, cst.READ_DISCRETE_INPUTS, 0, 8))
# 单个读写寄存器操作
# 写寄存器地址为0的保持寄存器
logger.info(master.execute(1, cst.WRITE_SINGLE_REGISTER, 0, output_value=20))
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 8))
# 写寄存器地址为0的线圈寄存器,写入内容为0(位操作)
logger.info(master.execute(2, cst.WRITE_SINGLE_COIL, 0, output_value=2))
logger.info(master.execute(2, cst.READ_COILS, 0, 1))
# # 多个寄存器读写操作
# # 写寄存器起始地址为0的保持寄存器,操作寄存器个数为4
logger.info(master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 0, output_value=[20,21,22,23]))
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 4))
# # 写寄存器起始地址为0的线圈寄存器
logger.info(master.execute(2, cst.WRITE_MULTIPLE_COILS, 0, output_value=[0,0,0,1]))
logger.info(master.execute(2, cst.READ_COILS, 0, 4))
except modbus_tk.modbus.ModbusError as e:
logger.error("%s- Code=%d" % (e, e.get_exception_code()))
import modbus_tk.defines
#modbus exception codes 异常代码
# 代码功能不合法
ILLEGAL_FUNCTION = 1
# 数据地址不合法
ILLEGAL_DATA_ADDRESS = 2
# 数据值不合法
ILLEGAL_DATA_VALUE = 3
# slave设备失败
SLAVE_DEVICE_FAILURE = 4
# 命令已收到
COMMAND_ACKNOWLEDGE = 5
# slave设备忙
SLAVE_DEVICE_BUSY = 6
# 内存奇偶校验差
MEMORY_PARITY_ERROR = 8
# supported modbus functions 功能代码
# 读线圈
READ_COILS = 1
# 离散读输入
READ_DISCRETE_INPUTS = 2
# 读保持寄存器
READ_HOLDING_REGISTERS = 3
# 读输入寄存器
READ_INPUT_REGISTERS = 4
# 写单一线圈
WRITE_SINGLE_COIL = 5
# 写单一寄存器
WRITE_SINGLE_REGISTER = 6
# *读例外状态
READ_EXCEPTION_STATUS = 7
DIAGNOSTIC = 8
# *报告slave的id
REPORT_SLAVE_ID = 17
# 写多个线圈
WRITE_MULTIPLE_COILS = 15
# 写多寄存器
WRITE_MULTIPLE_REGISTERS = 16
# *读写多个寄存器
READ_WRITE_MULTIPLE_REGISTERS = 23
# *设备信息
DEVICE_INFO = 43
# supported block types 支持的块类型
# 线圈
COILS = 1
# 离散输入
DISCRETE_INPUTS = 2
# 保持寄存器
HOLDING_REGISTERS = 3
# 模拟量输入
ANALOG_INPUTS = 4
000054-Rx:00 01 00 00 00 06 01 03 00 00 00 09
000055-Tx:00 01 00 00 00 15 01 03 12 00 14 00 15 00 16 00 17 00 00 00 00 00 00 00 00 00 00
000056-Rx:00 02 00 00 00 06 03 04 00 00 00 09
000057-Tx:00 02 00 00 00 15 03 04 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000058-Rx:00 03 00 00 00 06 02 01 00 00 00 08
000059-Tx:00 03 00 00 00 04 02 01 01 08
000060-Rx:00 04 00 00 00 06 02 05 00 01 FF 00
000061-Tx:00 04 00 00 00 06 02 05 00 01 FF 00
000062-Rx:00 05 00 00 00 06 04 02 00 00 00 08
000063-Tx:00 05 00 00 00 04 04 02 01 00
000064-Rx:00 06 00 00 00 06 01 06 00 00 00 14
000065-Tx:00 06 00 00 00 06 01 06 00 00 00 14
000066-Rx:00 07 00 00 00 06 01 03 00 00 00 08
000067-Tx:00 07 00 00 00 13 01 03 10 00 14 00 15 00 16 00 17 00 00 00 00 00 00 00 00
000068-Rx:00 08 00 00 00 06 02 05 00 00 FF 00
000069-Tx:00 08 00 00 00 06 02 05 00 00 FF 00
000070-Rx:00 09 00 00 00 06 02 01 00 00 00 01
000071-Tx:00 09 00 00 00 04 02 01 01 01
000072-Rx:00 0A 00 00 00 0F 01 10 00 00 00 04 08 00 14 00 15 00 16 00 17
000073-Tx:00 0A 00 00 00 06 01 10 00 00 00 04
000074-Rx:00 0B 00 00 00 06 01 03 00 00 00 04
000075-Tx:00 0B 00 00 00 0B 01 03 08 00 14 00 15 00 16 00 17
000076-Rx:00 0C 00 00 00 08 02 0F 00 00 00 04 01 08
000077-Tx:00 0C 00 00 00 06 02 0F 00 00 00 04
000078-Rx:00 0D 00 00 00 06 02 01 00 00 00 04
000079-Tx:00 0D 00 00 00 04 02 01 01 08
modbus tcp数据报文结构
请求:00 00 00 00 00 06 09 03 00 00 00 01
响应:00 00 00 00 00 05 09 03 02 12 34
一次modbus tcp读取保持寄存器的通信分析(省略了ip/tcp头):从左向右分析该数据报文:
请求:
00 00为此次通信事务处理标识符,一般每次通信之后将被要求加1以区别不同的通信数据报文;
00 00表示协议标识符,00 00为modbus协议;
00 06为数据长度,用来指示接下来数据的长度,单位字节;
09为设备地址,用以标识连接在串行线或者网络上的远程服务端的地址。以上七个字节也被称为modbus报文头;
03为功能码,此时代码03为读取保持寄存器数据;
00 00为起始地址;
00 01为寄存器数量,(word数量)。
响应:
00 00为此次通信事务处理标识符,应答报文要求与先前对应的请求保持一致;
00 00为协议标识符,与先前对应的请求保持一致;
00 05为数据长度,用来指示接下来数据的长度,单位字节;
09为设备地址,应答报文要求与先前对应的请求保持一致;
03为功能码,正常情况下应答报文要求与先前对应的请求保持一致,如果出错则返回80h+先前的功能码;
02指示接下来数据的字节长度;
12 34为被读取的保持寄存器中的数据值,即要求被读取的地址为00 00的保持寄存器中的数值为1234h。
000054-Rx:00 01 00 00 00 06 01 03 00 00 00 09
000055-Tx:00 01 00 00 00 15 01 03 12 00 14 00 15 00 16 00 17 00 00 00 00 00 00 00 00 00 00
请求
00 01 为此次通信的标识符
00 00 表示协议 ,其中 00 00 表示modbus协议
00 06 表示数据的长度
01 表示为1号设备地址
03 表示功能码 可以和上面的对应
00 00 为起始地址
00 09 为寄存器数量,默认10个也可以自己修改
响应
00 01 为此次通信的标识符
00 00 表示协议 ,其中 00 00 表示modbus协议
00 15 表示数据的长度,16进制中的表示21,后面数据长度为21
01 表示为1号设备地址
03 表示功能码 可以和上面的对应
12 表示接下来数据长度,16进制12表示18 ,后i面每两位代表一位数
00 14 表示20
00 15 表示21
.....
这些数据和python程序返回的一样
00 01 00 00 00 06 02 01 00 00 00 0A
00 01 00 00 00 05 02 01 02 3F 02
请求:
00 01为此次通信事务处理标识符,应答报文要求与先前对应的请求保持一致;
00 00为协议标识符,与先前对应的请求保持一致;
00 06为数据长度,用来指示接下来数据的长度,单位字节;
02为设备地址,用以标识连接在串行线或者网络上的远程服务端的地址。以上七个字节也被称为modbus报文头;
01为功能码,此时代码03为读取保持寄存器数据;
00 00为起始地址;
00 0A为线圈个数
响应:
00 01为此次通信事务处理标识符,应答报文要求与先前对应的请求保持一致;
00 00为协议标识符,与先前对应的请求保持一致;
00 05为数据长度,用来指示接下来数据的长度,单位字节;
02为设备地址,用以标识连接在串行线或者网络上的远程服务端的地址。以上七个字节也被称为modbus报文头;
01为功能码,此时代码03为读取保持寄存器数据;
02为数据长度
3F 02 为数据 代表1111 1100 0100 0000 每四位逆序组成一位16进制,
每两位16进制组成一组数据,组成的数据线先组成的16进制在后面,
例如1111 1100的逆序是1111(F) 0011(3) 最后组成数据为3F
000002-Rx:00 01 00 00 00 06 01 03 00 00 00 02
000003-Tx:00 01 00 00 00 07 01 03 04 26 D0 3F 9B
请求
00 01 为此次通信的标识符
00 00 表示协议 ,其中 00 00 表示modbus协议
00 06 表示数据的长度
01 表示为1号设备地址
03 表示功能码 可以和上面的对应
00 00 为起始地址
00 09 为寄存器数量,默认10个也可以自己修改,一个浮点数占四个字节,而一个寄存器只能存储两个字节的数据,所以需要2个寄存器来保存
响应
00 01 为此次通信的标识符
00 00 表示协议 ,其中 00 00 表示modbus协议
00 15 表示数据的长度,16进制中的表示21,后面数据长度为21
01 表示为1号设备地址
03 表示功能码 可以和上面的对应
04 表示接下来数据长度,16进制12表示18 ,后i面每两位代表一位数
26 D0 3F 9B 四位表示返回的浮点数数据
26d0 = 9936
3f9b = 16283
26d0= 0010011011010000(高位)
3f9b = 0011111110011011 (低位)
在intel处理器上,低字节在前面,有的平台是相反的。重组数据为
00111111100110110010011011010000
内容 SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
这里
S 代表符号位,1是负,0是正
E 偏移127的幂,二进制阶码=(EEEEEEEE)-127。
M 24位的尾数保存在23位中,只存储23位,最高位固定为1。此方法用最较少的位数实现了
对于该数据
0 01111111 00110110010011011010000
(1)0表示正
该数为正数
(2)01111111 = 127
127-127 = 0 向右偏移0位
(3)需要在00110110010011011010000前面加1.
数据为1.00110110010011011010000
(4)应为偏移量为0
最终数据为1.00110110010011011010000
import struct
def ReadFloat(*args,reverse=False):
for n,m in args:
n,m = '%04x'%n,'%04x'%m
if reverse:
v = n + m
else:
v = m + n
y_bytes = bytes.fromhex(v)
y = struct.unpack('!f',y_bytes)[0]
y = round(y,6)
return y
def WriteFloat(value,reverse=False):
y_bytes = struct.pack('!f',value)
# y_hex = bytes.hex(y_bytes)
y_hex = ''.join(['%02x' % i for i in y_bytes])
n,m = y_hex[:-4],y_hex[-4:]
n,m = int(n,16),int(m,16)
if reverse:
v = [n,m]
else:
v = [m,n]
return v
def ReadDint(*args,reverse=False):
for n,m in args:
n,m = '%04x'%n,'%04x'%m
if reverse:
v = n + m
else:
v = m + n
y_bytes = bytes.fromhex(v)
y = struct.unpack('!i',y_bytes)[0]
return y
def WriteDint(value,reverse=False):
y_bytes = struct.pack('!i',value)
# y_hex = bytes.hex(y_bytes)
y_hex = ''.join(['%02x' % i for i in y_bytes])
n,m = y_hex[:-4],y_hex[-4:]
n,m = int(n,16),int(m,16)
if reverse:
v = [n,m]
else:
v = [m,n]
return v
if __name__ == "__main__":
print(ReadFloat((9936, 16283)))
print(WriteFloat(3.16))
print(ReadDint((1734,6970)))
print(WriteDint(456787654))