前文【逗老师带你学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两个寄存器内存储着湿度、温度。

RS232读取称重传感器数据 python python如何获取传感器数据_传感器

三、数据整合展示到PRTG监控系统

1、串口服务器配置

如果你的温湿度传感器直接通过串口连接到监控服务器,可以跳过本小节。
如果你的问温湿度服务器通过串口转以太网映射到监控服务器,可以查阅说明书,我们在这用有人科技的一款转换器,举例配置如下:

1.1、服务端配置

RS232读取称重传感器数据 python python如何获取传感器数据_寄存器_02

1.2、客户端配置

通过服务端的IP地址+端口,为本地添加映射的虚拟串口。

RS232读取称重传感器数据 python python如何获取传感器数据_IT_03

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

RS232读取称重传感器数据 python python如何获取传感器数据_传感器_04