背景
本次任务内容是在RK3568这块板子上实现Modbus RTU和TCP这两种类型的主机及从机通信,其目的就是通过串口获取或者上送采集数据,经过半天的努力终于完成了以上任务。
接下来就对这两种类型进行逐个介绍,这里我们是基于Python的modbus_tk这个库来实现的,如果你的板子还没有这个库,可通过如下指令进行安装。
pip install modbus_tk
Modbus RTU
对于该类型的协议这里不多做介绍,实际上对于串口modbus_tk还是基于serial这个库的实现,因此,我们可先通过serial库验证串口是否通了,建议可按照如下顺序进行排查:
1.直接通过指令验证
echo TestCode > /dev/ttysWK0
2.通过serial接口验证
ser = serial.Serial('/dev/ttysWK0', 9600)
success_bytes = ser.write(b'This is data for test\r\n')
3.使用modbus_tk库实现最终的Modbus通信
Modbus RTU主机
import sys
import serial
import modbus_tk.modbus
import modbus_tk.defines as cst
import modbus_tk.modbus_rtu as modbus_rtu
#打开Modbus主机
master = modbus_rtu.RtuMaster(serial.Serial(port='/dev/ttysWK0', baudrate= 9600, bytesize=8, parity='N', stopbits=1))
master.open
master.set_timeout(3)
master.set_verbose(True)
#发送保持寄存器读取指令
master.execute(1, cst.READ_HOLDING_REGISTERS, 1, 5)
Modbus RTU从机
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu
import serial
import time
#打开Modbus从机
PORT = '/dev/ttysWK0'
modbusServ = modbus_rtu.RtuServer(serial.Serial(PORT),baudrate=9600,bytesize=8, parity='N', stopbits=1, xonxoff=0)
#启动从机服务器
modbusServ.start()
#创建从机设备
slave_1 = modbusServ.add_slave (1)
slave_1.add_block ("a",cst.ANALOG_INPUTS,200, 7)
slave_2 = modbusServ.add_slave(2)
slave_2.add_block ("b",cst.HOLDING_REGISTERS,200,7)
slave_5 = modbusServ.add_slave(5)
slave_5.add_block("c",cst.COILS,0,7)
#设定本地变量地址数值
while True:
slave_1.set_values ("a",200,1)
slave_1.set_values ("a",201,2)
slave_1.set_values ("a",202,3)
slave_1.set_values ("a",203,4)
slave_1.set_values ("a",204,5)
slave_1.set_values ("a",205,6)
slave_1.set_values ("a",206,7)
time.sleep(0.1)
Modbus TCP
顾名思义,这种类型的协议是基于TCP协议的,属于搭载在网络上的Modbus通信,这种类型依旧是基于modbus_tk的实现。
Modbus TCP主机
主机在以太网中的套接字属于客户端,主动去连接服务器(从机),实现代码如下:
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_tcp
#可使用默认的502作为协议端口
master = modbus_tcp.TcpMaster(host="192.168.200.65")
#也可以指定其它端口作为主机端口
#master = modbus_tcp.TcpMaster('192.168.200.65', 503)
master.set_timeout(5.0)
#执行主机发送--读取多个保持寄存器数值
master.execute(1,cst.READ_HOLDING_REGISTERS,0, 4)
注:这里所涉及的hook知识由于暂时没有用到,所以目前没有做研究。
Modbus TCP从机
从机是服务器角色,需要在本地建立,代码如下:
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_tcp
#创建从机
server = modbus_tcp.TcpServer(address='127.0.0.1',port=503)
#也可不指定端口,使用默认的502端口
#server = modbus_tcp.TcpServer(address='127.0.0.1')
server.start()
#添加从机设备
slave1 = server.add_slave(1)
slave1.add_block('a', cst.HOLDING_REGISTERS, 0, 100)
slave1.add_block('c', cst.COILS, 0, 100)
端口占用
由于涉及到以太网,又是基于Socket的连接,必然会遇到之前打开过的端口由于应用关闭或者重启时,端口并未被释放掉而导致再次打开应用时提示端口已被占用,因此,我们需要通过一段代码来主动释放被占用的端口,如下:
import os
import re
def kill_process(port):
ret = os.popen("netstat -nao|findstr " + str(port))
#解码方式为"gbk",否则输出乱码
str_list = ret.read().decode('gbk')
ret_list = re.split('',str_list)
try:
process_pid = list(ret_list[0].split())[-1]
os.popen('taskkill /pid ' + str(process_pid) + ' /F') #释放
except:
#未使用
总结
到此也就完成了Modbus的RTU和TCP的所有实现工作,有了协议的支持实现仅是基础,后续还要对寄存器数据以及应用层协议进行定义,在该层协议的基础上二次开发应用,这才是最重要也是最复杂的,毕竟Python已经通过库的形式简化了我们的很多工作。
需要注意,以上的参考代码都是基本的实现,并没有增加保护和检查以及异常识别机制,在实际的项目开发过程中是不能简单地使用以上代码,还是要通过完善的try机制来识别异常并能够保证系统的稳定运行以及异常中的自恢复。