前文【逗老师带你学IT】PRTG监控通过Python+TCP Modbus获取温湿度传感器数据中我们讲了如何通过Python读取支持TCP Modbus的传感器数据。本章我们讲解下如何读取Modbus RTU传感器的数据。
目录
- 一、Modbus TCP和RTU的区别
- 1、接口区别
- 2、数据帧区别
- 2.1、请求帧区别举例
- 2.2、应答帧区别举例
- 二、Python的Modbus RTU客户端
- 1、Python modbus_tk第三方库
- 2、确认温度传感器寄存器地址
- 三、数据整合展示到PRTG监控系统
- 1、串口服务器配置
- 1.1、服务端配置
- 1.2、客户端配置
- 2、代码部分
一、Modbus TCP和RTU的区别
Modbus协议包括ASCII、RTU、TCP等,并没有规定物理层。
1、接口区别
Modbus rtu和Modbus tcp两个协议的本质都是Modbus总线协议,都是靠Modbus寄存器地址来交换数据。
但二者所用的硬件接口不一样,Modbus RTU一般采用串口RS232C或RS485/422,而Modbus TCP一般采用以太网口。现在市场上有很多协议转换器,可以轻松的将这些不同的协议相互转换。
2、数据帧区别
2.1、请求帧区别举例
TCP请求帧:00 4b 00 00 00 06 01 03 00 00 00 02
事务处理标识符 | 协议标识符 | 数据长度 | 从机地址 | 功能码 | 寄存器起始地址 | 读取寄存器个数 |
00 4b | 00 00为modbus协议 | 00 06 | 01 | 03(读) | 0000 | 0002 |
RTU请求帧:01 03 00 00 00 02 C4 0B
从机地址 | 功能码 | 寄存器起始地址 | 读取寄存器个数 | CRC校验 |
01 | 03(读) | 0000 | 0002 | C40B |
2.2、应答帧区别举例
TCP应答帧:00 4b 00 00 00 07 01 03 04 01 b6 01 00
事务处理标识符 | 协议标识符 | 数据长度 | 从机地址 | 功能码 | 返回字节个数 | 寄存器40001数据 | 寄存器40002数据 |
00 4B | 00 00 | 00 07 | 01 | 03 | 04 | 01 B6 | 01 00 |
RTU应答帧:01 03 04 01 94 00 E2 3A 6A
从机地址 | 功能码 | 返回字节个数 | 寄存器40001数据 | 寄存器40002数据 | CRC校验 |
01 | 03 | 04 | 01 94 | 00 E2 | 3A 6A |
对比来看,请求帧中TCP比RTU异步串口增加了协议标识符和数据长度,这是符合TCP设计思路的。同时TCP比RTU取消了CRC校验,因为在TCP/IP层会自带CRC校验
二、Python的Modbus RTU客户端
1、Python modbus_tk第三方库
Python中提供了支持Modbus协议的第三方库——modbus_tk,简单的通过pip install modbus_tk
即可安装
2、确认温度传感器寄存器地址
对于你购买的支持TCP Modbus协议的温湿度传感器,请查阅说明书确认温度、湿度两个数据的寄存器地址。
比如,我这个温度传感器,很简单的就是第1、2两个寄存器内存储着湿度、温度。
三、数据整合展示到PRTG监控系统
1、串口服务器配置
如果你的温湿度传感器直接通过串口连接到监控服务器,可以跳过本小节。
如果你的问温湿度服务器通过串口转以太网映射到监控服务器,可以查阅说明书,我们在这用有人科技的一款转换器,举例配置如下:
1.1、服务端配置
1.2、客户端配置
通过服务端的IP地址+端口,为本地添加映射的虚拟串口。
2、代码部分
import json
import threading
from multiprocessing import Process,Manager,freeze_support
import time
import serial
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu
PORT = '/dev/cu.usbserial-AK08ROD4'
#PORT = 'COM6'
#修改为实际的串口编号
process_list = []
slave_num_list={
"1":"AL-SZ-8F-G5",
"2":"AL-SZ-10F-MDF"
}
#提前写入从站号和从站名称
def print_json(value_list):
try:
data={
"prtg": {
"result": []
}
}
result_data=[]
for i in value_list:
Temperature_data={
"Channel": "%s Temperature"%value_list[i][2],
"Unit": "Temperature",
"Mode":"Absolute",
"DecimalMode":"All",
"Float":1,
"LimitMode":1,
"LimitMaxWarning":35,
"LimitMinWarning":18,
"Value":int(value_list[i][1])/10
}
Humidity_data={
"Channel": "%s Humidity"%value_list[i][2],
"Unit": "Percent",
"Mode":"Absolute",
"DecimalMode":"All",
"Float":1,
"LimitMode":1,
"LimitMaxWarning":70,
"LimitMinWarning":10,
"Value":int(value_list[i][0])/10
}
data['prtg']['result'].append(Temperature_data)
data['prtg']['result'].append(Humidity_data)
print (json.dumps(data, sort_keys=True, indent=2))
except Exception as err:
raise err
def get_value(slave_num,value_list):
try:
master = modbus_rtu.RtuMaster(serial.Serial(port=PORT, baudrate=4800, bytesize=8, parity='N', stopbits=1, xonxoff=0))
master.set_timeout(5.0)
master.set_verbose(True)
Hold_value=master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 2)
Hold_value=list(Hold_value)
Hold_value.append(slave_num_list[slave_num])
except Exception as err:
#print([0,0,host_list[host_ip]])
value_list[slave_num]=[0,0,slave_num_list[slave_num]]
else:
value_list[slave_num]=Hold_value
#print(value_list)
def main():
try:
manager=Manager()
value_list=manager.dict()
for slave_num in slave_num_list:
p = Process(target=get_value, args=(slave_num,value_list))
process_list.append(p)
p.start()
for p in process_list:
p.join()
#print(value_list)
print_json(value_list)
except Exception as err:
data={
"prtg": {
"error": 1,
"text": str(err)
}
}
print (json.dumps(data, sort_keys=True, indent=2))
if __name__ == "__main__":
freeze_support()
main()
运行后给出的符合PRTG要求的json结构格式数据
{
"prtg": {
"result": [
{
"Channel": "AL-SZ-8F-G5 Temperature",
"DecimalMode": "All",
"Float": 1,
"LimitMaxWarning": 35,
"LimitMinWarning": 18,
"LimitMode": 1,
"Mode": "Absolute",
"Unit": "Temperature",
"Value": 22.3
},
{
"Channel": "AL-SZ-8F-G5 Humidity",
"DecimalMode": "All",
"Float": 1,
"LimitMaxWarning": 70,
"LimitMinWarning": 10,
"LimitMode": 1,
"Mode": "Absolute",
"Unit": "Percent",
"Value": 34.6
},
{
"Channel": "AL-SZ-10F-MDF Temperature",
"DecimalMode": "All",
"Float": 1,
"LimitMaxWarning": 35,
"LimitMinWarning": 18,
"LimitMode": 1,
"Mode": "Absolute",
"Unit": "Temperature",
"Value": 23.2
},
{
"Channel": "AL-SZ-10F-MDF Humidity",
"DecimalMode": "All",
"Float": 1,
"LimitMaxWarning": 70,
"LimitMinWarning": 10,
"LimitMode": 1,
"Mode": "Absolute",
"Unit": "Percent",
"Value": 37.1
}
]
}
}
Program ended with exit code: 0