目标:使用树莓派4B与CAN_HAT扩展板读取气体质量流量计的各项数据。
实验材料:树莓派4B,CAN_HAT扩展板,USB485转换器,MEMS气体质量流量计
树莓派相关库与例程在上次实验已经安装过了,步骤可参照官网:
RS485 CAN HAT - Waveshare Wiki
先用PC端测试一下通讯,产品说明书如下:
需要的数据有当前流量和当前总流量,复习一下之前学过的modbus协议,命令结构如下:
帧结构 = 地址 + 功能码 + 数据 + 校验
这个流量计的默认地址为255,也就是地址码为“FF”;查询功能码为“03”;以查询当前流量为例,存储当前流量值的寄存器地址为0x0002~0x0003,所以起始地址码为“00 02”;该值占用两个寄存器,所以偏移量为“00 02”;CRC算法得出其校验位数值,为“70 15”。
最终得出查询当前流量值命令为:
FF 03 00 02 00 02 70 15
PC端发送该命令:
返码中的数据位为“00 00 00 00”,表示当前流量值为0,获得的值除以1000后单位为SPLM,表示常温常压状态下的标准公升每分钟(L/min)。
同理得到查询累计流量值命令:
FF 03 00 04 00 03 51 D4
PC端发送命令:
返码中数据位占6个字节,因为总流量数据由3个寄存器存储,数据位为“00 00 00 00 00 04”,总流量值为4,获得的值除以1000后单位为NCM,也就是立方米,所以该值表示的是0.004立方米。
由于之前实验中控制RS485收发的python文件封装性比较差,所以我自己修改了一下,现在有以下几个文件:
Open_serial.py:
# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import serial
import time
class open_serial:
def __init__(self,EN_485=4,name="/dev/ttyAMA0",baud=9600):
self.__EN_485=EN_485
self.__name=name
self.__baud=baud
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.__EN_485,GPIO.OUT)
GPIO.output(self.__EN_485,GPIO.HIGH)
self.ser = serial.Serial(self.__name,self.__baud,timeout=1)
def get_serial(self):
print("Serial Number:",self.__EN_485,",Serial Name:",self.__name,",Baud Rate:",self.__baud,"\n")
这个文件控制串口的开启,串口号、串口名以及波特率、超时等在这里可以修改。
Receive_data.py:
# -*- coding:utf-8 -*-
import sys
sys.path.append(r'/home/pi/RS485_CAN_HAT_Code/RS485/python')
from Open_serial import open_serial
class receive_data:
def __init__(self,EN_485=4,name="/dev/ttyAMA0",baud=9600):
self.__os=open_serial(EN_485,name,baud)
self.__ser=self.__os.ser
def get_data(self):
#while 1:
Str = self.__ser.readall()
if Str:
self.res=Str.hex()
#print(self.res)
if len(self.res) == 14:
self.__data=self.res[6:10]
elif len(self.res) == 18:
self.__data=self.res[6:14]
elif len(self.res) == 22:
self.__data=self.res[6:18]
else:
print("Error!")
self.__data=int(self.__data,16)
print("Received Data is:",self.__data)
return self.__data
这里用相应的串口接收数据并截取数据位,只要是RS485通讯都可以通过这个方法截取出数据位,然后转化为十进制,返回这个十进制的结果。
Send_command.py:
# -*- coding:utf-8 -*-
import sys
sys.path.append(r'/home/pi/RS485_CAN_HAT_Code/RS485/python')
from Open_serial import open_serial
class send_command:
def __init__(self,EN_485=4,name="/dev/ttyAMA0",baud=9600):
self.__os=open_serial(EN_485,name,baud)
self.__ser=self.__os.ser
#self.__os.get_serial()
def send(self,strInput):
self.__strInput=strInput
str=bytes.fromhex(self.__strInput)
self.__ser.write(str)
print("Send Successfully!")
#s=send_command()
#s.send("01 03 00 4B 00 02 B4 1D")
使用相应串口发送指定的命令。
Run.py:
# -*- coding:utf-8 -*-
import sys
import time
sys.path.append(r'/home/pi/RS485_CAN_HAT_Code/RS485/python')
from Send_command import send_command
from Receive_data import receive_data
class run:
r=receive_data()
#result=t0.get_result()
#print(result)
s1=send_command()
s1.send("FF 03 00 02 00 02 70 15")
result1=r.get_data()
time.sleep(5)
s1.send("FF 03 00 04 00 03 51 D4")
result2=r.get_data()
print("result1:",float(result1)/1000,"\nresult2:",float(result2)/1000)
入口函数,相当于C++的main函数,模拟用户的操作,这里先是实例化receive_data对象和send_command对象,然后调用send发送查询命令,调用get_data获取接受到的数据,然后进行单位换算之后输出。
改完之后有点面向对象的样子了,但是封装性还是不够好,比如单位换算这样的操作应该由一个专门的类来完成,但我还没想好怎么写~所以这算是初代版本吧~
运行结果:
与仪表显示无差别: