Raspberry Pi 4B communicates with gas mass flowmeter through RS485 (modbus RTU protocol)

Objective: Use Raspberry Pi 4B and CAN_HAT expansion board to read various data of gas mass flowmeter.

Experimental materials: Raspberry Pi 4B, CAN_HAT expansion board, USB485 converter, MEMS gas mass flow meter

 Raspberry Pi related libraries and routines have been installed in the last experiment, the steps can refer to the official website:

RS485 CAN HAT - Waveshare Wiki

1. PC serial port test

First use the PC to test the communication, the product manual is as follows:

 The required data includes the current flow and the current total flow. Review the modbus protocol that you have learned before. The command structure is as follows:

Frame structure = address + function code + data + checksum

The default address of this flowmeter is 255, that is, the address code is "FF"; the query function code is "03"; taking the query of the current flow as an example, the address of the register storing the current flow value is 0x0002~0x0003, so the starting address code It is "00 02"; this value occupies two registers, so the offset is "00 02"; the CRC algorithm obtains its check digit value, which is "70 15".

Finally, the command to query the current flow value is:

FF 03 00 02 00 02 70 15

 The PC side sends this command:

 The data bit in the return code is "00 00 00 00", which means that the current flow value is 0, and the obtained value is divided by 1000, and the unit is SPLM, which means standard liters per minute (L/min) under normal temperature and pressure.

In the same way, the command to query the cumulative flow value is obtained:

FF 03 00 04 00 03 51 D4

 Send command from PC:

 The data bits in the return code occupy 6 bytes, because the total flow data is stored by 3 registers, the data bits are "00 00 00 00 00 04", the total flow value is 4, the obtained value is divided by 1000 and the unit is NCM, That is cubic meters, so this value represents 0.004 cubic meters.

2. Communication between Raspberry Pi and flowmeter

Since the encapsulation of the python file controlling RS485 sending and receiving in the previous experiment was relatively poor, I modified it myself, and now there are the following files:

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")
        

This file controls the opening of the serial port, the serial port number, serial port name, baud rate, timeout, etc. can be modified here.

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
                
        

Here, the corresponding serial port is used to receive data and intercept the data bits. As long as it is RS485 communication, the data bits can be intercepted by this method, and then converted into decimal, and the decimal result is returned.

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")

Use the corresponding serial port to send the specified command.

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)

The entry function, which is equivalent to the main function of C++, simulates the user's operation. Here, the receive_data object and the send_command object are instantiated first, then send is called to send the query command, and get_data is called to obtain the received data, and then output after unit conversion.

After the modification, it looks a bit object-oriented, but the encapsulation is still not good enough. For example, operations such as unit conversion should be done by a special class, but I haven't figured out how to write it~ so this is the first generation version~

operation result:

 There is no difference from the meter display:

 

 

 

Guess you like

Origin blog.csdn.net/qq_43824745/article/details/126391725