简介
这篇文章写如何使用Python语言 与PLC 进行Modbus RTU通信。
相比ModBus TCP,Modbus RTU要慢很多,而且设备之间的通信连接也比较麻烦,还需要掌握一定的串行相关的硬件以及协议等知识。现实应用中有大量的设备只支持串行通信,所以Modbus RTU还是非常有必要学习和掌握的。
硬件清单
笔记本电脑一部台达DVP系列PLC一台USB转RS232电缆一根Moxa RS232转RS485 转换器一个
补充说明:
台达PLC作为从站设备 型号为DVP60ES2,其拥有3个com口,com1为RS232接口,com2,com3 为RS485接口
Delta-DVP-PLC
由于笔记本没有串口,使用USB转RS232电缆实现串口功能
本例程中会使用RS485接口和PLC通信,那么就需要使转换器将RS232 信号转换为 RS485。为何不直接用RS232进行modbus通信呢,因为RS232只支持两个设备之间一对一的通信,只有将RS232转换成RS485 才能具备多个设备站点组网的能力,才可以实现远程多机通信。
通信线接线
1.使用一根带有屏蔽的双绞线,棕色线接PLC的COM2 D+端子,蓝色线接PLC的COM2 D-端子,按照标准要在最后一个从站的D+与D-端子之间接一个120欧姆的终端电阻来消除线路阻抗带来的反射波。(本例程只有一个从站,未接电阻)
2.双绞线的另一端接在信号转换器上,其中棕色线接R+/D+端子,蓝色线接R-/D-端子。
3.将USB转RS232电缆和信号转换器插在一起,将电缆的USB端插在电脑U口上。
PLC上电,并将拨码开关打到run挡位。
串行通信参数基本概念
Modbus RTU是基于串行通信技术之上的一种应用层协议,而串行通信的基本参数有:
波特率:数据传输速率(表示每秒传输的位bit的个数)
数据位:通信中传输一组数据包含的位数。
停止位:通信数据包或者数据帧的最后一位。
奇偶校验位:奇偶校验位是串行通信中的一种数据检错方式。
串行通信常用的通信格式为( 9600,8,N,1)表示波特率为9600b/s,8位数据位,无奇偶校验,1位停止位。
从机设备串行端口参数设置
在多机串行网络中,包括主站在内的每一台设备的串行参数必须一致,否则无法通信。
Modbus串行网络中每一个从站都必须有一个唯一性的ID来标记自己在网络中的身份,这就是站号的意义。
Modbus TCP 是基于以太网协议,网络中每一个设备都有唯一的IP地址,所以ModBus TCP从站不需要站号。
台达PLC的com2参数需要编程来设置成我们想要的,PLC作为从站,同时要给PLC分配一个站号,并规定通信模式为RTU模式。
台达PLC-com2通信参数设置
说明:
PLC上电之后的第一 个扫描周期(M1002)为TRUE,用于触发初始化程序。16#81 = 2# 1000 0001 =(9600,8,N,1),将参数16#81mov至D1120(com2参数寄存器),详情可查阅PLC手册将M1143设为TRUE表示 设置com2为RTU模式将M1120设为TRUE表示 设置com2通信保持将十进制 2 mov至D1121 表示设PLC的站号为 2将十进制 500 mov至D1129 通信超时时间为 500ms
PLC通信初始化程序写好之后下载至PLC中。
当程序运行之后PLC的COM2端口参数就被设置成9600/8-N-1;RTU模式;2号站。
Python主站程序
首先要在自己的电脑中部署python环境,请参阅历史文章《基于python的MQTT客户端》一文。
python代码中会用到modbus_tk库,如何安装modbus_tk请翻看《使用Python操作Ge Fanuc PLC》内容。
同时代码中需要使用到serial模块,是一个Python操作串行端口的库,一般安装python时会默认安装。
Modbus TRU的python程序示例:
import serial
import modbus_tk
import modbus_tk.defines as CST
from modbus_tk import modbus_rtu
def mod(PORT="com4"):#在 PORT 参数位置写自己串行端口的端口号 如 com1 com2 或者 com3
response = [] #名为response的列表用来存储反馈数据。
alarm = "" #用来存放异常信息
try:
# 设定串口参数———— 此处的设置必须和从机设备的端口参数一致
# 示例中参数为 9600 8 N 1
master = modbus_rtu.RtuMaster(serial.Serial(
port=PORT,
baudrate=9600,
bytesize=8,
parity='N',
stopbits=1
))
master.set_timeout(3.0) #timeout
master.set_verbose(True)
# 读保持寄存器
response = master.execute(
2, # 从站的站号 --->取决于从站的站号设置
CST.READ_HOLDING_REGISTERS, # modbus功能码 --->可以直接用功能码替代
0x1000, # 从站Modbus寄存器起始地址 --->数据格式 可以是 DEC 可以是 HEX
4 # 被操作的寄存器的数量 --->包含起始地址的连续n个寄存器
)
print(response)
alarm = "ok" #正常返回提示
return list(response),alarm
except Exception as exc:
print(str(exc))
alarm = (str(exc))
return response, alarm #异常返回故障信息
if __name__ == "__main__":
mod() # 如果需要连续执行 将mod()函数包含在循环语句的循环体中即可
这是一个示例程序,如果需要进行其他操作只需要修改第27行response = master.execute( )语句括号中的内容即可。
注意:PORT参数取决自己电脑的端口号,如:com1,com2。
功能码说明:
# 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
异常代码说明:
#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
操作示例1:
# 操作2号从站;功能码 10#15 = hex0f;起始地址Y0 = 10#1280; 连续操作数12个 ;输出值放在一个列表[]中。
response = master.execute(2, CST.WRITE_MULTIPLE_COILS, 1280,12, output_value = [1,1,1,0,1,0,1,0,1,1,1,1])
执行后效果如图:
写Y输出
操作示例2:
# 目标设备:2号从站,从T0 开始 连续写 2 个定时器寄存器 T0 地址1536 功能码 16 = hex10
response = master.execute(2, CST.WRITE_MULTIPLE_REGISTERS, 1536,2, output_value = [1234,9876])
print(response)
video: https://mp.weixin.qq.com/mp/readtemplate?t=pages/video_player_tmpl&action=mpvideo&auto=0&vid=wxv_1990240136034385921
操作示例3:
# 目标设备:2号从站,从D0 开始连续写6个寄存器 D0 地址 = 4096 功能码 16 = hex10
response = master.execute(2, CST.WRITE_MULTIPLE_REGISTERS, 4096,4, output_value = [9999,8888,7777,6666,5555,4444])
print(response)
video: https://mp.weixin.qq.com/mp/readtemplate?t=pages/video_player_tmpl&action=mpvideo&auto=0&vid=wxv_1990235072234389507
操作示例4:
# 目标设备:2号从站,从D0 开始连续读取6个寄存器 D0 地址 = 4096 功能码 3= hex03
response = master.execute(2, CST.READ_HOLDING_REGISTERS, 4096,6)
print(response)
video: https://mp.weixin.qq.com/mp/readtemplate?t=pages/video_player_tmpl&action=mpvideo&auto=0&vid=wxv_1990237245638836228
使用Python操作GE Fanuc Rx3i PLC
基于Python的MQTT客户端
PowerFlex 变频器参数备份&恢复
TDengine部署与基本运维
Redis学习笔记
MariaDB数据库 部署与配置指南 - CentOS 8
物联网-MQTT服务器搭建-阿里云篇
ModBus仿真环境的搭建
ModBus通信协议之功能码&寄存器 |