当前位置:   article > 正文

python modbus 实现TCP 通信_python modbus tcp写

python modbus tcp写

下载对应pip

pip install modbus_tk
  • 1

实现RTU通信

https://blog.csdn.net/qq_40423339/article/details/96289321
  • 1

由于没有硬件设备,采用软件模拟,软件下载地址为

链接:https://pan.baidu.com/s/10C_3VL04Ycb5C_-YfrPj_w 
提取码:jhuv 
复制这段内容后打开百度网盘手机App,操作更方便哦
  • 1
  • 2
  • 3

在通过TCP通信的时候我们需要下载modbusslave和modbuspol,但modbusslave有一个缺陷,就是最多只能连接100个寄存器或者线圈,我们可以通过使用nodbus-TCP client Tester来模拟更多的线圈或者寄存器,他最多支持65536个线圈或者寄存器。

下载安装好开始连接,第一次连接需要激活

打开slave,点击connection-connect,配置好参数选择OK,开始监听
在这里插入图片描述

模拟创建一个HOLDING_REGISTERS

在这里插入图片描述
简单修改设备id为1,function为03 Holding Register,点击ok

点击左上角file-new依次创建 以下 模拟器

在这里插入图片描述在这里插入图片描述在这里插入图片描述

点击Display-communication开始显示协议传输信息

在这里插入图片描述

编写python代码


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()))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

我对modbus_tk.defines中文件进行简单翻译

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

程序输出结果如下

在这里插入图片描述

modbus slave监听到数据如下

在这里插入图片描述

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 

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

modbus tcp数据格式

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。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

对寄存器的数据进行分析

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
  • 1
  • 2

请求

00 01 为此次通信的标识符
00 00 表示协议 ,其中 00 00 表示modbus协议
00 06 表示数据的长度
01 表示为1号设备地址
03 表示功能码 可以和上面的对应
00 00 为起始地址
00 09 为寄存器数量,默认10个也可以自己修改
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

响应

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程序返回的一样
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

对线圈数据进行分析,输入状态的和线圈的分析方法一样:

00 01 00 00 00 06 02 01 00 00 00 0A
00 01 00 00 00 05 02 01 02 3F 02
  • 1
  • 2

请求:

00 01为此次通信事务处理标识符,应答报文要求与先前对应的请求保持一致;
00 00为协议标识符,与先前对应的请求保持一致;
00 06为数据长度,用来指示接下来数据的长度,单位字节;
02为设备地址,用以标识连接在串行线或者网络上的远程服务端的地址。以上七个字节也被称为modbus报文头;
01为功能码,此时代码03为读取保持寄存器数据;
00 00为起始地址;
00 0A为线圈个数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

响应:

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

线圈实际值为下图所示
在这里插入图片描述

对寄存器读取浮点数数据进行分析

寄存器的值

在这里插入图片描述

对返回数据进行分析

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
  • 1
  • 2

请求

00 01 为此次通信的标识符
00 00 表示协议 ,其中 00 00 表示modbus协议
00 06 表示数据的长度
01 表示为1号设备地址
03 表示功能码 可以和上面的对应
00 00 为起始地址
00 09 为寄存器数量,默认10个也可以自己修改,一个浮点数占四个字节,而一个寄存器只能存储两个字节的数据,所以需要2个寄存器来保存
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

响应

 00 01 为此次通信的标识符
00 00 表示协议 ,其中 00 00 表示modbus协议
00 15 表示数据的长度,16进制中的表示21,后面数据长度为21
01 表示为1号设备地址
03 表示功能码 可以和上面的对应
04 表示接下来数据长度,16进制12表示18 ,后i面每两位代表一位数
26 D0 3F 9B  四位表示返回的浮点数数据
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

对26 d0 3f 9b 进行解析

26d0 = 9936
3f9b = 16283

把对26 d0 3f 9b 转换成二进制

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表示正

该数为正数
  • 1

(2)01111111 = 127

  127-127 = 0 向右偏移0位
  • 1

(3)需要在00110110010011011010000前面加1.

数据为1.00110110010011011010000
  • 1

(4)应为偏移量为0

最终数据为1.00110110010011011010000
  • 1

(5)准换成10进制取小数点后6位的
在这里插入图片描述

我在网上也找了个python脚本转化

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))


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

运行结果
在这里插入图片描述

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号