飞道的博客

python modbus 实现RTU 通信

716人阅读  评论(0)

下载对应pip

pip install modbus_tk

实现TCP通信的地址

https://blog.csdn.net/qq_40423339/article/details/96284979

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

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

在通过rtu通信的时候我们需要下载modbusslave和modbuspol和vspd.exe

安装vspd.exe 用于模拟串口

在没有安装前可以看到我们电脑没有对应的串口

安装好通过vspd添加串口


可以看见电脑上多了两个端口

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

打开slave,点击connection-connect,配置好参数选择OK,开始监听

模拟创建一个HOLDING_REGISTERS


简单修改设备id为1,function为03 Holding Register,点击ok

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

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

编写python代码

# -*- coding: utf_8 -*-


import serial
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu


def mod(PORT="com2"):
    red = []
    alarm = ""
    try:
        # 设定串口为从站
        master = modbus_rtu.RtuMaster(serial.Serial(port=PORT,
                                                    baudrate=9600, bytesize=8, parity='N', stopbits=1))
        master.set_timeout(5.0)
        master.set_verbose(True)

        # 读保持寄存器
        red = master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 9)  # 这里可以修改需要读取的功能码
        print(red)
        alarm = "正常"
        return list(red), alarm

    except Exception as exc:
        print(str(exc))
        alarm = (str(exc))

    return red, alarm  ##如果异常就返回[],故障信息


if __name__ == "__main__":
    mod()

运行结果

modbus slave监听到的数据如下

000080-Rx:01 03 00 00 00 09 85 CC
000081-Tx:01 03 12 00 14 00 15 00 16 00 17 00 00 00 00 00 00 00 00 00 00 11 6B

对数据进行分析

000080-Rx:01 03 00 00 00 09 85 CC
000081-Tx:01 03 12 00 14 00 15 00 16 00 17 00 00 00 00 00 00 00 00 00 00 11 6B

详细的报文分析见

https://blog.csdn.net/ouyangxin95/article/details/78169380/
https://blog.csdn.net/qq_26093511/article/details/79251320

读寄存器

请求

01 从机地址
03 功能码
00 00 起始寄存器
00 09 读取寄存器个数
85 cc  CRC校验

响应

01 从机地址
03 功能码
12 返回数据个数,16进制12表示18, 每两位16进制表示一个数,共有9个数
00 14 表示20
00 15 表示21
.....
这些数据和python程序返回的一样
11 6B CRC校验

读线圈的和输入状态的分析一样

000660-Rx:02 01 00 00 00 02 BD F8
000661-Tx:02 01 01 03 11 CD

请求

02 从机地址
01 功能码
00 00 起始寄存器
00 02 读取寄存器个数
BD F8 CRC校验

响应

02 从机地址
01 功能码
01 返回数据个数
03 返回数据  03 起始代表读取了两个寄存器值都为1 显示为11,
     (3F 02 为数据 代表1111 1100 0100 0000 每四位逆序组成一位16进制,
     每两位16进制组成一组数据,组成的数据线先组成的16进制在后面,
     例如1111 1100的逆序是1111(F) 0011(3)  最后组成数据为3F)

读寄存器里面小数数据

000004-Rx:01 03 00 00 00 04 44 09
000005-Tx:01 03 08 38 86 40 4E C6 A8 40 91 5B 67

请求

01 从机地址
03 功能码
00 00 起始寄存器
00 04 读取寄存器个数
44 09  CRC校验

响应

01 从机地址
03 功能码
08 返回数据个数
38 86 40 4E 第一个小数值因为一个浮点数占4个字节,而一个寄存器只能保存两个字节的数据,,因此使用两个寄存器保存一位小数
C6 A8 40 91 第二个小数值
5B 67 CRC校验

对返回数据进行分析

内容 SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
这里
S 代表符号位,1是负,0是正
E 偏移127的幂,二进制阶码=(EEEEEEEE)-127。
M 24位的尾数保存在23位中,只存储23位,最高位固定为1。此方法用最较少的位数实现了
在intel处理器上,低字节在前面,有的平台是相反的。

原寄存器值为

38 86 40 4E

3886

0011100010000110

404E

0100000001001110

总数据(小端(404E 3886))

0 10000000 10011100011100010000110
0 128 1.10011100011100010000110

128-127 = 1

右移一位

最终数据,精度问题会有缺失

  11.0011100011100010000110
  3.2221999168395996

C6 A8 40 91

C6A8
1100011010101000
4091
0100000010010001
总数据(小端 4091 C6A8)
0 10000001 00100011100011010101000
0 129 1.00100011100011010101000
129-127 = 2 右移两位
100.100011100011010101000
4.555500030517578

我也从网上找了个能直接转换,不用你分析的代码

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((14470, 16462)))
    print(ReadFloat(( 50856, 16529)))
    print(WriteFloat(3.2222))
    print(ReadDint((1734,6970)))
    print(WriteDint(456787654))


运行结果
可以看到与我们分析的一致


转载:https://blog.csdn.net/qq_40423339/article/details/96289321
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场