一、目标
使用电脑上的MThings(摩尔信使:Modbus通信助手)、网络调试助手(NetAssist)、Python代码分别实现对连接在卓岚串口服务器上的RS485温湿度传感器的温湿度读取。
二、准备工作
2.1 硬件
1.导线;
2.网线;
3.电脑;
4.开关电源(220V交流 转 24V直流);
5.卓岚RS485单串口服务器(ZLAN5143D);
6.温湿度传感器(5V-36V);
输入寄存器功能定义 | ||
协议地址 | PLC地址 | 功能描述 |
0000H | 30001 | 温度值 单位:℃ 解析方法:固定1位小数点 (1)正温度:寄存器数据<10000 例:寄存器值为250,实际湿度值: 250x0.1=25℃ (2)负温度:寄存器数据>=10000 例:寄存器值为10250 实际湿度值: -1*(10250 -10000) *0.1=-25℃ |
0001H | 30002 | 湿度值. 单位:%RH 解析方法:固定1位小数点 例:寄存器值为500,实际湿度值: 500*0.1=50 (%RH) |
0002H | 30003 | 温度值 单位:℃ 解析方法:32 位浮点数,大端模式 |
0003H | 30004 | |
0004H | 30005 | 湿度值 单位:%RH 解析方法:32 位浮点数,大端模式 |
0005H | 30006 |
保持寄存器功能定义 | ||
协议地址 | PLC地址 | 功能描述 |
0000H | 30001 | 温度值 单位:℃ 解析方法:固定1位小数点 (1)正温度:寄存器数据<10000 例:寄存器值为250,实际湿度值: 250x0.1=25℃ (2)负温度:寄存器数据>=10000 例:寄存器值为10250 实际湿度值: -1*(10250 -10000) *0.1=-25℃ |
0001H | 30002 | 湿度值. 单位:%RH 解析方法:固定1位小数点 例:寄存器值为500,实际湿度值: 500*0.1=50 (%RH) |
0002H | 30003 | 温度值 单位:℃ 解析方法:32 位浮点数,大端模式 |
0003H | 30004 | |
0004H | 30005 | 湿度值 单位:%RH 解析方法:32 位浮点数,大端模式 |
0005H | 30006 | |
000AH | 40011 | RS485总线地址/站号(1 ~255)。 出厂默认:1 注:此参数掉电保存,修改后重新上电即可生效 |
000BH | 40012 | 0:4800 1:9600 (出厂默认) 2:14400 3:19200 4:38400 5:56000 6:57600 7:115200 注:此参数掉电保存,修改后重新.上电即可生效 |
2.2 软件
1.ZLVirCom563(卓岚RS485单串口服务器设备管理工具);
2.MThings(摩尔信使:Modbus通信助手);
3.NetAssist(网络调试助手)
4.配置好Python的VScode,并且安装好第三方库:socket、struct、crcmod
三、接线图
四、通信参数配置
4.1 电脑通信参数配置
1.确保网线将电脑和卓岚单串口服务器接好(串口服务器上指示灯Link为绿色或者蓝色)
2.打开“网络和Internet”设置——高级网络设置——更改适配器选项——找到“以太网”图标,右键属性;
3.选择“Internet 协议版本4(TCP/IPv4)”;
4.点选“使用下面的IP地址”——输入IP地址和子网掩码(注意:IP地址必须和串口服务器的地址在同一个网段下。即192.168.1是一样的,后面的字段不一样)——点击“确定”——完成电脑的以太网IP设置;
4.2 卓岚RS485单串口服务器通信参数配置
1.电脑打开“ZLVirCom5.63.exe”软件;
2.点击“设备管理”——“自动搜索”——搜索得到串口服务器设备;
3.点击“编辑设备”——修改IP地址(这里设置192.168.1.10,端口4196,需要与电脑配置的IP在同一个网段下,否则通信不上)——修改波特率为9600(温湿度传感器的默认波特率就是9600)——其他默认即可——点击“修改设置”;
五、MThings操作
5.1 通信配置
1.打开MThings调试软件——点击右上角的通道管理;
2. 进入通道界面管理的小窗口后,点击新增网络连接;
3.链接模式选择“TCP客户端”;传输协议选择“MODBUS-RTU”(表面上使用TCP通信,但其通信的报文是是MODBUS-RTU的);目标域名/IP和目标端口填写“IP192.168.1.10 端口4196”(四、通信参数配置——4.2卓岚RS485单串口服务器通信参数配置——3);填写完基本信息后点击确定;检查链接是否添加成功;
4.回到MThings主界面——点击左上角“+”符号——进入“添加设备”小窗口——通道选择我们上一步创建的网络链接“NET001”——设备类型“模拟主机”(因为卓岚模块是属于从机)——点击“添加”;
5.上一步添加成功之后,再进入到“通道管理”里面可发现“NET001”的状态已变成了绿色的“已连接”,就表示MThings调试软件已经与卓岚串口服务器连接上了,接下来就创建数据表,并进行通信了;
6.回到MTings主界面,然后点击界面上方的“数据”——进入到“数据”选项卡后——点击上方的“+”符号添加数据行;
7.温湿度值都存储于输入寄存器(保持寄存器)中,地址0存储温度值,地址1存储湿度值,我们可以先创建这4个数据行,2个输入寄存器的温度和湿度行,2个保持寄存器的温度和湿度行;
我们还可以添加两个输入寄存器起始地址为0的数据行、两个保持寄存器起始地址为0的数据行,然后寄存器数量我们可以设置为2,意思是温湿度两个值一起读过来变成32位整数,自己可以选择性试一试。
温湿度传感器的出厂设置的寄存器在保持寄存器,起始地址10。
温湿度传感器的波特率设置的寄存器在保持寄存器,起始地址11。
5.2 读取温湿度值(ModbusRTU读数据)
5.1步骤完成之后,我们就可以使用MThinngs书写数据,并观察其报文格式(后面可以使用网络调试助手来验证),首先将右上方的“报文”勾选上。
5.2.1 读取温度值(读输入寄存器,起始地址0,数量1)
客户端(电脑)发送:01 04 00 00 00 01 31 CA
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
04 | 功能码,读输入寄存器的功能码是04,1个字节 |
00 00 | 起始地址0,2个字节 |
00 01 | 读取的寄存器数量1个,2个字节 |
31 CA | CRC校验码,2个字节 |
服务器(温湿度传感器)响应:01 04 02 01 1C B9 69
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
04 | 功能码,读输入寄存器的功能码是04,1个字节 |
02 | 数据长度:2个字节 |
01 1C | 16进制温度值,转换成10进制是284,2个字节 |
B9 69 | CRC校验码,2个字节 |
5.2.2 读取湿度值(读输入寄存器,起始地址1,数量1)
客户端(电脑)发送:01 04 00 01 00 01 60 0A
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
04 | 功能码,读输入寄存器的功能码是04,1个字节 |
00 01 | 起始地址1,2个字节 |
00 01 | 读取的寄存器数量1个,2个字节 |
60 0A | CRC校验码,2个字节 |
服务器(温湿度传感器)响应:01 04 02 02 C3 F8 01
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
04 | 功能码,读输入寄存器的功能码是04,1个字节 |
02 | 数据长度:2个字节 |
02 C3 | 16进制湿度值,转换成10进制是707,2个字节 |
F8 01 | CRC校验码,2个字节 |
5.2.3 读取温湿度值(读输入寄存器,起始地址0,数量2)
客户端(电脑)发送:01 04 00 00 00 02 71 CB
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
04 | 功能码,读输入寄存器的功能码是04,1个字节 |
00 00 | 起始地址0,2个字节 |
00 02 | 读取的寄存器数量2个,2个字节 |
71 CB | CRC校验码,2个字节 |
服务器(温湿度传感器)响应:01 04 04 01 1C 02 C3 7A 8F
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
04 | 功能码,读输入寄存器的功能码是04,1个字节 |
04 | 数据长度:4个字节 |
01 1C | 16进制温度值,转换成10进制是284,2个字节(地址为0的输入寄存器值) |
02 C3 | 16进制湿度值,转换成10进制是707,2个字节(地址为1的输入寄存器值) |
7A 8F | CRC校验码,2个字节 |
5.2.4 读取温度值(读保持寄存器,起始地址0,数量1)
客户端(电脑)发送:01 03 00 00 00 01 84 0A
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
03 | 功能码,读保持寄存器的功能码是03,1个字节 |
00 00 | 起始地址0,2个字节 |
00 01 | 读取的寄存器数量1个,2个字节 |
84 0A | CRC校验码,2个字节 |
服务器(温湿度传感器)响应:01 03 02 01 1C B8 1D
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
03 | 功能码,读输入寄存器的功能码是03,1个字节 |
02 | 数据长度:2个字节 |
01 1C | 16进制温度值,转换成10进制是284,2个字节 |
B8 1D | CRC校验码,2个字节 |
5.2.5 读取湿度值(读保持寄存器,起始地址1,数量1)
客户端(电脑)发送:01 03 00 01 00 01 D5 CA
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
03 | 功能码,读保持寄存器的功能码是03,1个字节 |
00 01 | 起始地址1,2个字节 |
00 01 | 读取的寄存器数量1个,2个字节 |
D5 CA | CRC校验码,2个字节 |
服务器(温湿度传感器)响应:01 03 02 02 C3 F9 75
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
03 | 功能码,读保持寄存器的功能码是03,1个字节 |
02 | 数据长度:2个字节 |
02 C3 | 16进制湿度值,转换成10进制是707,2个字节 |
F9 75 | CRC校验码,2个字节 |
5.2.6 读取温湿度值(读保持寄存器,起始地址0,数量2)
客户端(电脑)发送:01 03 00 00 00 02 C4 0B
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
03 | 功能码,读保持寄存器的功能码是03,1个字节 |
00 00 | 起始地址0,2个字节 |
00 02 | 读取的寄存器数量2个,2个字节 |
C4 0B | CRC校验码,2个字节 |
服务器(温湿度传感器)响应:01 03 04 01 1C 02 C3 7B 38
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
03 | 功能码,读保持寄存器的功能码是03,1个字节 |
04 | 数据长度:4个字节 |
01 1C | 16进制温度值,转换成10进制是284,2个字节(地址为0的保持寄存器值) |
02 C3 | 16进制湿度值,转换成10进制是707,2个字节(地址为1的保持寄存器值) |
7B 38 | CRC校验码,2个字节 |
5.3 设置温湿度传感器出厂设置与波特率(ModbusRTU写数据)
5.3.1 恢复出厂设置
客户端(电脑)发送:01 06 00 0A 00 01 68 08
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
06 | 功能码,写保持寄存器的功能码是06,1个字节 |
00 0A | 起始地址10,2个字节(十六进制A就是十进制10) |
00 01 | 写入保持寄存器的数值1,2个字节 |
68 08 | CRC校验码,2个字节 |
服务器(温湿度传感器)响应:01 06 00 0A 00 01 68 08
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
06 | 功能码,写保持寄存器的功能码是06,1个字节 |
00 0A | 起始地址10,2个字节 |
00 01 | 写入保持寄存器成功的数值1,2个字节 |
68 08 | CRC校验码,2个字节 |
5.3.2 设置波特率
客户端(电脑)发送:01 06 00 0B 00 01 39 C8
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
06 | 功能码,写保持寄存器的功能码是06,1个字节 |
00 0B | 起始地址11,2个字节(十六进制B就是十进制11) |
00 01 | 写入保持寄存器的数值1,2个字节 |
39 C8 | CRC校验码,2个字节 |
服务器(温湿度传感器)响应:01 06 00 0B 00 01 39 C8
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
06 | 功能码,写保持寄存器的功能码是06,1个字节 |
00 0B | 起始地址11,2个字节(十六进制B就是十进制11) |
00 01 | 写入保持寄存器成功的数值1,2个字节 |
39 C8 | CRC校验码,2个字节 |
六、网络调试助手操作
通过以上MThings操作温湿度传感器的步骤可以发现,每读写一次数据,我们的客户端电脑就会向温湿度传感器发送一串十六进制数据,温湿度传感器作为响应也会发送一串十六进制数据给电脑。使用MThings软件我们只要关心我们需要读写的寄存器地址、数量以及我们要写入的数值,而报文怎么编写不是我们需要关心的。
但如果我们打算使用网络TCP的形式去进行通信的话,那么我们需要就需要知道ModbusRTU的报文是如何编写的,并且知道报文如何解析。
1.打开网络调试助手(NetAssist.exe),按下列步骤完成操作。
客户端(电脑)发送:01 04 00 00 00 01 31 CA
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
04 | 功能码,读输入寄存器的功能码是04,1个字节 |
00 00 | 起始地址0,2个字节 |
00 01 | 读取的寄存器数量1个,2个字节 |
31 CA | CRC校验码,2个字节 |
服务器(温湿度传感器)响应:01 04 02 01 1C B9 69
01 | 从站地址1,一般默认都是1,1个字节 |
---|---|
04 | 功能码,读输入寄存器的功能码是04,1个字节 |
02 | 数据长度:2个字节 |
01 1C | 16进制温度值,转换成10进制是284,2个字节 |
B9 69 | CRC校验码,2个字节 |
2.这里只展示了读取温湿度传感器的温度值(输入寄存器地址0,读取数量1个),如果想要读取湿度值,或者想要将某个值写入指定的保持寄存器中,我们可以根据上面的ModbusRTU报文格式,自己定一条写保持寄存器的报文,并发送给ModbusRTU设备即可,并学会解析ModbusRTU报文格式,知道数据值存放在什么地方,并转换为我们能看懂的10进制。
七、Python操作
如果我们想要用这个温湿度传感器做更多的事情,最简单的比如说,我想每隔一段时间采集一次温湿度值,并记录在excel表中。
那么以上两款调试助手均不能做到,可以使用任意一种编程语言来实现。这里使用的是Python编程语言,虽然Python可以安装第三方库pymodbus实现modbus通信,但是这个库使用ModbusTCP通信时,其通信协议只能是ModbusTCP,而不能选择ModbusRTU协议。
所以使用Python时,我们需要使用的第三方库是socket,然后在TCP网络通信的基础上以ModbusRTU为通信报文,也就是需要自己写程序解析读取报文,还有生成报文,其中包含了CRC校验码的生成。
7.1 第三方库的安装
如果没有安装第三方库的话,请按照以下方式进行安装,我这里使用pip指令进行安装,win+R键打开运行窗口,输入cmd,回车打开命令行窗口,第三方库可以在里面安装,以下安装指令都在cmd窗口中执行:
1.使用pip指令安装socket库,用于网络通信:
pip install socket
2.使用pip指令安装struct库,用于解析十六进制报文,并生成报文:
pip install struct
3.使用pip指令安装crcmod库,用于生成CRC校验码:
pip install crcmod
7.2 编写Modbus通信代码
以下是实例代码:
import socket,struct,crcmod ''' struct打包解包格式: x:跳过一个字节 b:有符号字节 B:无符号字节 h:有符号短整数(2字节) H:无符号短整数(2字节) i:有符号整数(4字节) I:无符号整数(4字节) f:单精度浮点数(4字节) d:双精度浮点数(8字节) ''' #CRC-MODBUS校验码计算 def crc16(data): #参数data是十六进制数据 crc16 = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0xFFFF, xorOut=0x0000) #固定参数 crc = crc16(data) return struct.pack('H',crc) #得到的校验码(转换成无符号短整形2个字节的十六进制数) #关于TCP通信,走ModbusRTU协议的类 class ModbusTcpRtuClinet(): #初始化函数 def __init__(self,IP,port): dest_address = (IP, port) #TCP通信所需要的远程IP与端口 # 创建一个TCP socket对象,创建UDP套接字 #socket.AF_INET IPv4 #socket.AF_INET6 IPv6 #socket.SOCK_STREAM TCP协议 #socket.SOCK_DGRAM UDP协议 self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #选择IPv4模式、TCP通信协议 self.client_socket.connect(dest_address) #连接到TCP客户端 #读多个输入寄存器 #address:读取输入寄存器的起始地址 #count:读取输入寄存器的个数 #unit:从站地址(默认为1) def read_input_registers(self,address,count,unit=1): #向从站发出读数据请求 send_data_byte = struct.pack('>B',unit) + struct.pack('>B',3) + struct.pack('>H',address) + struct.pack('>H',count) send_data_byte += crc16(send_data_byte) #将读输入寄存器输入的报文加上CRC校验码 self.client_socket.send(send_data_byte) #发送这一串16进制的读输入寄存器报文 #接收从站数据 receive_data_byte = self.client_socket.recv(1024) #接收数据,一次读取1024个字节 #接收RTU从站响应的报文(高字节在后,低字节在前) data_len = struct.unpack('>xxB%sxx'%(count*"xx"),receive_data_byte)[0] #解析读取的寄存器数据总长度(单位:字节) unpack_format = '>BBB%sH'%(int(data_len/2)*'h') #2个字节一个h,短整型 return list(struct.unpack(unpack_format,receive_data_byte)[3:-1]) #解析数据最终将我们要的寄存器数据提取出来 #读多个保持寄存器 #address:读取输入寄存器的起始地址 #count:读取输入寄存器的个数 #unit:从站地址(默认为1) def read_holding_registers(self,address,count,unit=1): #向从站发出读数据请求 send_data_byte = struct.pack('>B',unit) + struct.pack('>B',3) + struct.pack('>H',address) + struct.pack('>H',count) send_data_byte += crc16(send_data_byte) #将读保持寄存器输入的报文加上CRC校验码 self.client_socket.send(send_data_byte) #发送这一串16进制的读保持寄存器报文 #接收从站数据 receive_data_byte = self.client_socket.recv(1024) #接收数据,一次读取1024个字节 #接收RTU从站响应的报文(高字节在后,低字节在前) data_len = struct.unpack('>xxB%sxx'%(count*"xx"),receive_data_byte)[0] #解析读取的寄存器数据总长度(单位:字节) unpack_format = '>BBB%sH'%(int(data_len/2)*'h') #2个字节一个h,短整型 return list(struct.unpack(unpack_format,receive_data_byte)[3:-1]) #解析数据最终将我们要的寄存器数据提取出来 #写单个保持寄存器 def write_holding_register(self,address,value,unit=1): #向从站发出读数据请求 send_data_byte = struct.pack('>B',unit) + struct.pack('>B',6) + struct.pack('>H',address) + struct.pack('>h',value) send_data_byte += crc16(send_data_byte) #将读保持寄存器输入的报文加上CRC校验码 self.client_socket.send(send_data_byte) #发送这一串16进制的读保持寄存器报文 #接收从站数据 receive_data_byte = self.client_socket.recv(1024) #接收数据,一次读取1024个字节 #接收RTU从站响应的报文(高字节在后,低字节在前) if receive_data_byte == send_data_byte: #如果接受到的数据跟发送出去的报文数据一样,则说明保持寄存器写入成功 return True #保持寄存器写入成功返回True else: return False #保持寄存器写入失败返回False #关闭客户端 def close(self): self.client_socket.close() #关闭客户端 #开始通信读写数据 client = ModbusTcpRtuClinet("192.168.1.10", 4196) #连接到设备,填写IP和端口 print("温度(输入寄存器,地址0,数量1):",client.read_input_registers(0,1)) #读输入寄存器地址0,数量1 print("湿度(输入寄存器,地址1,数量1):",client.read_input_registers(1,1)) #读输入寄存器地址1,数量1 print("温湿度(输入寄存器,地址0,数量2):",client.read_input_registers(0,2)) #读输入寄存器地址0,数量2 print("温度(保持寄存器,地址0,数量1):",client.read_holding_registers(0,1)) #读保持寄存器地址0,数量1 print("湿度(保持寄存器,地址1,数量1):",client.read_holding_registers(1,1)) #读保持寄存器地址1,数量1 print("温湿度(保持寄存器,地址0,数量2):",client.read_holding_registers(0,2)) #读保持寄存器地址0,数量2 print("恢复出厂设置(保持寄存器,地址10,写入数值1):",client.write_holding_register(10,1)) #写保持寄存器地址10,值为1,恢复出厂设置 print("波特率是否设置成功(保持寄存器,地址11,写入数值1):",client.write_holding_register(11,1)) #写保持寄存器地址11,值为1,修改温湿度传感器波特率,修改成功返回True,失败返回False client.close() #关闭客户端
运行结果:
从终端运行的结果可以看见,已经成功读取和写入寄存器值,读取到的寄存器值存储在python列表中,也就是存储在中括号中。目前获取到的温湿度值还需要除以10,得到的才是真正的温湿度值。写入寄存器成功返回True,写入失败返回False。
八、结论
本篇文章主要讲述了通过MThings、网络调试助手、编写Python脚本来实现TCP上的ModbusRTU协议通信,比较特殊的地方是使用了以太网转RS485模块。除了这些还有各种各样的方式可以实现,其核心只要在于ModbusRTU通信的报文发送和接收。如果没有使用串口服务器的话,可以使用Mthings、串口调试助手、Python中的pymodbus第三方库来实现。以此简单的温湿度传感器为例,希望能为大家提供有价值的参考和帮助。如有问题可进行评论或者私信我,看到后我会第一时间回复大家。