The LabV pump series from Innofluid (formerly known as Shenchen Baoding) is an advanced series of flow measurement peristaltic pumps featuring a 4.3-inch color touch screen for intuitive control. It displays flow data, settings, and system information simultaneously, with animations to show the working state. Equipped with intelligent calibration, online micro-adjustment, and three measurement modes—fixed volume, fixed time and volume, and timer start/stop—it’s highly versatile. Compatible with various pump heads and offering multiple external control options, the LabV series is ideal for laboratory use, equipment integration, and industrial production.
In this blog post, we will explore how to control the LabV pumps using the MODBUS RTU protocol via a common programming platform: Python.
It’s worth noting that while there may be slight differences in registers set up and commands among various Lab pump series (such as LabF, LabN, LabK, etc.), they all share the same MODBUS RTU communication protocol. Therefore, the insights provided in this article are relevant and applicable to users of any Innofluid Lab pump series.
Why Choose a LabV Series Peristaltic Pump?
The LabV Peristaltic Pump stands out as a top choice for fluid handling needs in laboratories and industrial settings alike. Boasting a new servo motor for enhanced accuracy and durability, coupled with a 4.3" LCD touchscreen interface, this pump offers intuitive control for precise dispensing and fixed volume modes. Crafted from laboratory-grade ABS, it ensures both ease of use and robust performance.
With flow rates spanning from 0.0053 µL/min to 775 mL/min, and the flexibility to accommodate up to 8 channels, it caters to a wide range of applications. Additionally, its industrial-grade construction, dynamic display, and intelligent calibration functionality make it a reliable asset for various fluid transfer tasks.
Whether for research, equipment support, or industrial production, the LabV series of peristaltic pumps stands as a very good solution for fluid handling challenges.
What do you Need to Communicate with a LabV Pump?
To communicate with a LabV pump using the MODBUS RTU protocol, you will need the following:
- A LabV pump (a LabV1-III Intelligent Flow Rate Peristaltic Pump will be utilized herein) with a pump head and a tubing of your choice
- A serial cable that connects to your computer or device
- An RS485 or an RS232 wiring onto the external control port on the rear of the pump
- A programming platform (Python in the case herein)
- A basic understanding of the MODBUS RTU communication protocol detailed below 👇.
How to Set Up the Communication Port ?
Before you can start sending and receiving data from the pump, you need to set up the communication port correctly. Here are the steps to follow:
- Connect the cable for the RS232 or RS485 communication to the external control port on the rear of the pump
- Turn on the pump
- In the communication setting interface on the pump screen, choose RS232 or RS485 as your communication interface (details of the RS232 and RS485 wiring are presented in the tables below)
- No matter if you choose RS232 or RS485, the communication protocol is standard MODBUS RTU
- Set the baud rate
- Click on the Slave No. button to set the address of the peristaltic pump (range: 1-32)
- Select communication enable ON
- Return to the main interface as the pump only receives communication control when in the main interface, it is invalid for the communication control in other settings interfaces.
RS232 Wiring Details | |
---|---|
Terminal | Description |
GND | Communication ground terminal. |
TXD | Master sending, peristaltic pump receive signal terminal. |
RXD | Peristaltic pump sending, master receive signal terminal. |
RS485 Wiring Details | |
---|---|
Terminal | Description |
GD1 | RS485 signal ground. |
A+ | Connect RS485 A+ terminal. |
B- | Connect RS485 B- terminal. |
The LabV Series MODBUS RTU Communication Protocol
The LabV Peristaltic Pump series offers a comprehensive command set for precise control and monitoring via the MODBUS RTU protocol. This command set encompasses various directives categorized to facilitate manipulation of the pump’s functionalities, covering actions such as motor speed settings, flow rate configurations, pump head selections, and calibration routines. Each command, identified by unique parameters, ensures precise control over the pump’s operation.
Detailed explanations and further information regarding the MODBUS RTU protocol for the control of the LabV pumps will be provided below and can also be found in the communication protocol’s documentation.
MODBUS RTU Standard Communication Format
The message format is the one described in the table below.
Field | Size | Description |
---|---|---|
Slave address | 1 Byte | Address of the pump. The No. range is 1-32 and 0 is used for broadcast. |
Function code | 1 Byte | Instruction for the slave device. 03H: Read holding registers 06H: Write single register 10H: Write multiple registers 02H: Read discrete inputs (Read bits of data) 05H: Write single bit to register |
Data area | 0 – 252 Bytes | Data to be transmitted (variable length) |
CRC Check | 2 Bytes | Cyclic Redundancy Check for error detection CRC low (1 Byte): Lower byte of CRC CRC high (1 Byte): Higher byte of CRC |
CRC Check with CRC-16 in Python
CRC (Cyclic Redundancy Check) is a vital technique for detecting errors in data transmission. In the following, we’ll demonstrate how to implement CRC-16, the one used in the American binary synchronous system, using Python. In CRC-16, the error-detection algorithm employs a polynomial G(X) = X16 + X15 + X2 + 1 to generate the checksum. This polynomial defines the mathematical operations performed on the bits of the message during the CRC calculation process. An overview of the algorithm is detailed below:
- Initialize a 16-bit register with the hexadecimal value FFFF (all ones), referred to as the CRC register.
- XOR the first 8 bytes of the message with the low byte of the CRC register. Store the result back in the CRC register.
- Shift the CRC register one bit to the right, with the most significant bit (MSB) set to zero.
- Check the least significant bit (LSB) of the CRC register:
- If LSB is 0, repeat step 3 (shift operation).
- If LSB is 1, XOR the CRC register with the polynomial value 0xA001 (1010 0000 0000 0001).
- Repeat steps 3-4, for a total of 8 shifts, to process each byte.
- Repeat steps 2-5 for each byte in the message until all bytes have been processed.
- The final content of the CRC register is the CRC value.
Communication Settings
Each byte of data has a structure consisting of 1 start bit, 8 data bits, 1 parity bit, and 1 stop bit. Bits are sent in order from the least significant bit (LSB) to the most significant bit (MSB). The possible communication baud rates include 1200, 2400, 4800, and 9600 bits/s.
For integers (2 bytes), the data is arranged with the second byte first followed by the first byte. For example, the hexadecimal value 2378H is sent as 23H 78H. For long integers and floats (4 bytes), the data is arranged with the fourth byte first, followed by the third, second, and first bytes.
MODBUS Message RTU Frame Format
The MODBUS message RTU frame format is the one described in the table below.
Field | Size | Description |
---|---|---|
Start | ≥ 3.5 characters | Indicates the start of transmission |
Address | 8 Bits | Address of the slave device |
Function Code | 8 Bits | Instruction for the slave device |
Data | N x 8 Bits | Data to be transmitted (variable length, N bytes) |
CRC Check Code | 16 Bits | Cyclic Redundancy Check for error detection |
End | ≥ 3.5 characters | Indicates the end of transmission |
🚨 Ensure that the message frame is transmitted continuously without idle spaces between characters greater than 1.5 character times. Incomplete message frames, indicated by excessive idle space, should be discarded by the receiving node.
Abnormal Reaction
When a host sends a request for data and the slave receives abnormal data, it should trigger an abnormal reaction. If the address code sent from the host is incorrect, meaning it does not correspond to any address code among the slaves, or if the data received by the slave fails the CRC check, no abnormal code is returned. In such cases, the host should initiate a superior reaction process.
In the function code area, the abnormal reaction function code is defined as the normal reaction function code plus 80H.
In the data area, the abnormal code to be returned is defined as in the table below.
Code | Name | Meaning |
---|---|---|
01H | Illegal function code | The function code received by the peristaltic pump is not permitted, except for 03H, 06H, or 10H. |
02H | Illegal data address | The register address received by the peristaltic pump is not allowed. |
03H | Illegal data value | Written data does not meet the operating range expected by the peristaltic pump. |
06H | Slave (peristaltic pump) busy | The peristaltic pump is currently busy and unable to complete the command due to a conflicting state. |
🚨The peristaltic pump only accepts MODBUS commands through the Main Interface; other interfaces do not receive messages.
Holding Register Address and Content
Below are the tables detailing basic and calibration parameters setting respectively.
Basic Parameters Setting
Address (Decimal) | Name | Range | Data Type |
---|---|---|---|
1000 | Pump Head | Relative to Chart 1 | unsigned short int (2 Bytes) |
1001 | Tubing Size | Relative to Chart 1 | unsigned short int (2 Bytes) |
1002 | Motor Speed | 0.1-600 rpm | float (4 Bytes) |
1004 | Flow Rate | 0.1-99999 mL | float (4 Bytes) |
1007 | Back Suction Angle | 0-360° | unsigned short int (2 Bytes) |
1008 | Start/Stop Control | 1: Start, 0: Stop | unsigned short int (2 Bytes) |
1009 | Direction Control | 1: Clockwise, 0: Counter-clockwise | unsigned short int (2 Bytes) |
1010 | Full Speed Running | 1: Start full speed, 0: Stop full speed | unsigned short int (2 Bytes) |
1015 | Set Flow Volume | 0-99999 mL | float (4 Bytes) |
1018 | Working Time | 0.1-9999 s | float (4 Bytes) |
1020 | Working Mode | 0: Transferring, 1: Fixed volume measurement, 2: Fixed time and volume | unsigned short int (2 Bytes) |
1021 | Pause Time | 0.1-9999 s | float (4 Bytes) |
1023 | Copy Numbers | 0-9999 times, 0 means infinite | unsigned short int (2 Bytes) |
💡 Important Notes:
- Registers 1015 and 1018 are invalid when the working mode is set to transferring.
- Register 1018 is invalid when the working mode is set to Fixed volume measurement.
- Registers 1002 and 1004 are invalid when the working mode is set to Fixed time and volume.
- Ensure that the register data is set according to the chart provided, and avoid sending multiple register setups in a single order.
Calibration Parameters Setting
Address (Decimal) | Name | Range | Data Type |
---|---|---|---|
2001 | Testing time | 0.5-9999 s | unsigned short int (2 Bytes) |
2002 | Start/Stop test | 1: Start, 0: Stop | unsigned short int (2 Bytes) |
2003 | Actual volume | 0-9999 mL | float (4 Bytes) |
2005 | Restore Defaults | 1: Restore calibration | unsigned short int (2 Bytes) |
2006 | Micro Adjustment | 1: Increase, 0: Decrease | unsigned short int (2 Bytes) |
Pump Head and Tubing Number Chart
Pump Head Name | Pump Head Type | Tubing Size | Tubing Specific |
---|---|---|---|
EasyPumpI | 0 | 13 | 13# |
14 | 14# | ||
19 | 19# | ||
16 | 16# | ||
25 | 25# | ||
17 | 17# | ||
18 | 18# | ||
EasyPumpII | 1 | 15 | 15# |
24 | 24# | ||
35 | 35# | ||
36 | 36# | ||
EasyPumpIII | 2 | 13 | 13# |
14 | 14# | ||
19 | 19# | ||
16 | 16# | ||
25 | 25# | ||
17 | 17# | ||
18 | 18# | ||
EasyPumpIV | 3 | 15 | 15# |
24 | 24# | ||
35 | 35# | ||
36 | 36# | ||
EasyPumpV | 4 | 13 | 13# |
14 | 14# | ||
19 | 19# | ||
16 | 16# | ||
25 | 25# | ||
EasyPumpVI | 5 | 13 | 13# |
14 | 14# | ||
19 | 19# | ||
16 | 16# | ||
25 | 25# | ||
2*EasyPumpI | 6 | 13 | 13# |
14 | 14# | ||
19 | 19# | ||
16 | 16# | ||
25 | 25# | ||
17 | 17# | ||
18 | 18# | ||
2*EasyPumpII | 7 | 15 | 15# |
24 | 24# | ||
35 | 35# | ||
36 | 36# | ||
2*EasyPumpIII | 8 | 13 | 13# |
14 | 14# | ||
19 | 19# | ||
16 | 16# | ||
25 | 25# | ||
17 | 17# | ||
18 | 18# | ||
YZ1515x | 12 | 13 | 13# |
14 | 14# | ||
19 | 19# | ||
16 | 16# | ||
25 | 25# | ||
17 | 17# | ||
18 | 18# | ||
YZ2515x | 13 | 15 | 15# |
24 | 24# | ||
DZ25-3L | 18 | 15 | 15# |
24 | 24# | ||
35 | 35# | ||
36 | 36# | ||
UC15 | 20 | 19 | 19# |
16 | 16# | ||
25 | 25# | ||
17 | 17# | ||
18 | 18# | ||
UC25 | 21 | 24 | 24# |
35 | 35# | ||
36 | 36# | ||
SN15 | 22 | 14 | 14# |
16 | 16# | ||
SN25 | 23 | 24 | 24# |
💡 Important Note:
- The table above is not exhaustive and includes only a subset of pump heads and tubing sizes. For the complete list, please refer to the documentation.
Examples of Command Strings
Example 1: Start the pump
- Address: 01, indicates the device address to which the command is being sent.
- Function Code: 06, used to write a single value to a register.
- Register Address: 03F0, register address corresponding to the control register for starting the pump (1008 in hexadecimal).
- Data: 0001, written to the register to start the pump (1: Start, 0: Stop).
- CRC: 487D, value used to ensure data integrity.
Therefore, the corresponding command string to start the pump is: 01 06 03 F0 00 01 48 7D.
Example 2: Set the pump speed to 20 rpm
- Address: 01, indicates the device address to which the command is being sent.
- Function Code: 10, used to write values to multiple registers.
- Register Address: 03EA, register address corresponding to the speed setting register (1002 in hexadecimal).
- Number of Registers: 0002, two registers will be written to.
- Byte Count: 04, number of data bytes to be written.
- Data: 41A00007, represents the speed setting in float format corresponding to 20 RPM.
- CRC: D76E, value used to ensure data integrity.
Therefore, the corresponding command string to set the pump speed to 20 rpm is: 01 10 03 EA 00 02 04 41 A0 00 07 D7 6E.
These 2 examples and other examples of command strings are presented in the table below.
Task | Command String |
---|---|
Start pump | 01 06 03 F0 00 01 48 7D |
Set direction to CCW | 01 06 03 F1 00 00 D8 7D |
Set direction to CW | 01 06 03 F1 00 01 19 BD |
Set speed to 20 RPM | 01 10 03 EA 00 02 04 41 A0 00 07 D7 6E |
Set flow rate to 50.0 mL/min | 01 10 03 EC 00 02 04 42 48 00 00 7D 2C |
Stop pump | 01 06 03 F0 00 00 89 BD |
Start full speed running | 01 06 03 F2 00 01 E9 BD |
How to Control the LabV Pumps Using Python ?
The LabV pump series can be controlled remotely using the MODBUS RTU protocol via Python, offering greater flexibility and convenience for running complex pumping programs with loops, pauses, and varying speeds/flow rates. This approach is particularly useful when integrating multiple devices, as it allows for the automation of sequences involving all connected equipment.
In this section, a dedicated custom Python library that utilizes the MODBUS RTU protocol will be introduced, allowing one to open the serial port and send control command strings to the LabV pump. We’ll cover examples that show how to set speed, start and stop the pump, set pump head and tube size, etc., providing a practical guide to using Python for controlling a LabV pump.
🚨🚨🚨 Important Note
- When viewing this code, please be aware that <, >, and & symbols might not display correctly due to formatting issues. These should appear as standard Python code syntax, so make sure to interpret < as <, > as >, and & as &.
'''
-----------------------------------------------------------
LABV Peristaltic Pump Control Library Using Modbus Protocol
-----------------------------------------------------------
'''
import serial
import struct
#------------------------------------------------------#
# LIBRARY MANAGING THE COMMUNICATION WITH THE PUMP #
#------------------------------------------------------#
class LABV:
# Initialize the object and establish a serial connection with the corresponding data format
def __init__(self, port, address=1, baudrate=9600, parity='E', bytesize=8, stopbits=1, timeout=1):
self.ser = serial.Serial(port=port, baudrate=baudrate, parity=parity,
bytesize=bytesize, stopbits=stopbits, timeout=timeout)
self.address = address
# Connect the pump
def connect(self):
if not self.ser.is_open:
self.ser.open()
# Close the serial connection
def disconnect(self):
if self.ser.is_open:
self.ser.close()
# Calculate the CRC (Cyclic Redundancy Check) for data integrity as
# explained in "The MODBUS RTU Communication Protocol" section
def _calculate_crc(self, data):
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x0001:
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return crc.to_bytes(2, byteorder='little')
# Convert data into bytes for communication
def _prepare_data(self, data, data_type):
if data_type == 'float':
return struct.pack('>f', data)
elif data_type == 'int':
return data.to_bytes(2, byteorder='big')
else:
raise ValueError("Data type not supported.")
# Construct, send commands to the pump and verify the response
def _send_command(self, function_code, register_address, data, data_type):
data_bytes = self._prepare_data(data, data_type)
payload = bytearray([self.address, function_code])
payload.extend(register_address.to_bytes(2, byteorder='big'))
if function_code == 0x10: # Write multiple registers
number_of_registers = len(data_bytes) // 2
payload.extend(number_of_registers.to_bytes(2, byteorder='big'))
payload.append(len(data_bytes))
payload.extend(data_bytes)
else:
payload.extend(data_bytes)
crc = self._calculate_crc(payload)
payload.extend(crc)
print(f"Sending payload: {payload.hex()}") # Debug: Print the payload
self.ser.write(payload)
response = self.ser.read(8) # Adjust as per the device response length
if len(response) != 8:
raise ValueError("Invalid response length")
if self._calculate_crc(response[:-2]) != response[-2:]:
raise ValueError("Invalid CRC")
return response
#------------------------------#
# BASIC PARAMETERS SETTING #
#------------------------------#
# Start the pump
def start_pump(self):
print("Starting pump...")
response = self._send_command(0x06, 0x03F0, 0x0001, 'int')
print(f"Start pump response: {response.hex()}")
# Stop the pump
def stop_pump(self):
print("Stopping pump...")
response = self._send_command(0x06, 0x03F0, 0x0000, 'int')
print(f"Stop pump response: {response.hex()}")
# Set the pump head
def set_pump_head(self, pump_head):
print(f"Setting pump head to {pump_head}...")
response = self._send_command(0x06, 0x03E8, pump_head, 'int')
print(f"Set pump head response: {response.hex()}")
# Set the tube size
def set_tube_size(self, tube_size):
print(f"Setting tube size to {tube_size}...")
response = self._send_command(0x06, 0x03E9, tube_size, 'int')
print(f"Set tube size response: {response.hex()}")
# Set the direction of rotation
def set_direction(self, direction):
print(f"Setting direction to {direction}...")
data = 0x0001 if direction.upper() == "CW" else 0x0000
response = self._send_command(0x06, 0x03F1, data, 'int')
print(f"Set direction response: {response.hex()}")
# Set the speed of the pump in RPM
def set_speed(self, speed):
print(f"Setting speed to {speed} RPM...")
response = self._send_command(0x10, 0x03EA, speed, 'float')
print(f"Set speed response: {response.hex()}")
# Set the flow rate in mL/min
def set_flow_rate(self, flow_rate):
print(f"Setting flow rate to {flow_rate} mL/min...")
response = self._send_command(0x10, 0x03EC, flow_rate, 'float')
print(f"Set flow rate response: {response.hex()}")
# Set the flow volume in mL
def set_flow_volume(self, volume):
print(f"Setting flow volume to {volume} mL...")
response = self._send_command(0x10, 0x03F3, volume, 'float')
print(f"Set flow volume response: {response.hex()}")
# Set the working time in seconds
def set_working_time(self, time):
print(f"Setting working time to {time} seconds...")
response = self._send_command(0x10, 0x03FA, time, 'float')
print(f"Set working time response: {response.hex()}")
# Set the working mode
def set_working_mode(self, mode):
"""
Working modes:
0: Transferring
1: Fixed volume measurement
2: Fixed time and volume
"""
if mode not in [0, 1, 2]:
raise ValueError("Invalid working mode. Must be 0, 1, or 2.")
print(f"Setting working mode to {mode}...")
response = self._send_command(0x06, 0x03FC, mode, 'int')
print(f"Set working mode response: {response.hex()}")
# Set the pause time in seconds
def set_pause_time(self, time):
print(f"Setting pause time to {time} seconds...")
response = self._send_command(0x10, 0x03FD, time, 'float')
print(f"Set pause time response: {response.hex()}")
# Set the number of copies
def set_copy_numbers(self, numbers):
print(f"Setting copy numbers to {numbers}...")
response = self._send_command(0x06, 0x03FF, numbers, 'int')
print(f"Set copy numbers response: {response.hex()}")
# Set the back suction angle in degrees
def set_back_suction_angle(self, angle):
print(f"Setting back suction angle to {angle} degrees...")
response = self._send_command(0x06, 0x03EF, angle, 'int')
print(f"Set back suction angle response: {response.hex()}")
# Start or stop full speed running
def set_full_speed_running(self, start):
state = "Start" if start else "Stop"
print(f"{state} full speed running...")
data = 0x0001 if start else 0x0000
response = self._send_command(0x06, 0x03F2, data, 'int')
print(f"{state} full speed running response: {response.hex()}")
#---------------------------------------#
# CALIBRATION PARAMETERS SETTING UP #
#---------------------------------------#
# Set the testing time in seconds
def set_testing_time(self, time):
print(f"Setting testing time to {time} seconds...")
response = self._send_command(0x06, 0x07D1, time, 'int')
print(f"Set testing time response: {response.hex()}")
# Start the calibration test
def start_test(self):
print("Starting test...")
response = self._send_command(0x06, 0x07D2, 0x0001, 'int')
print(f"Start test response: {response.hex()}")
# Stop the calibration test
def stop_test(self):
print("Stopping test...")
response = self._send_command(0x06, 0x07D2, 0x0000, 'int')
print(f"Stop test response: {response.hex()}")
# Set the actual volume in mL
def set_actual_volume(self, volume):
print(f"Setting actual volume to {volume} mL...")
response = self._send_command(0x10, 0x07D3, volume, 'float')
print(f"Set actual volume response: {response.hex()}")
# Restore calibration defaults
def restore_defaults(self):
print("Restoring defaults...")
response = self._send_command(0x06, 0x07D5, 0x0001, 'int')
print(f"Restore defaults response: {response.hex()}")
# Perform micro adjustment (increase or decrease)
def micro_adjustment(self, increase=True):
action = "Increase" if increase else "Decrease"
print(f"{action} micro adjustment...")
data = 0x0001 if increase else 0x0000
response = self._send_command(0x06, 0x07D6, data, 'int')
print(f"{action} micro adjustment response: {response.hex()}")
#-----------------------------------------------------------------------#
# EXAMPLES ON HOW TO USE THE DEFINED CLASS TO CONTROL THE LABV PUMP #
#-----------------------------------------------------------------------#
if __name__ == "__main__":
pump = LABV(port='COM6', address=1) # Change port and address according to your setup
pump.connect()
try:
pump.set_pump_head(5)
pump.set_tube_size(16)
pump.set_direction("CW")
pump.set_speed(50)
pump.set_flow_rate(50.0)
pump.set_back_suction_angle(0)
pump.set_full_speed_running(True)
pump.start_pump()
except Exception as e:
print(f"An error occurred: {e}")
finally:
pump.stop_pump()
pump.disconnect()
This Python library, to control the LabV pump series via the MODBUS RTU protocol, allows for precise and automated control over various pump parameters such as speed, direction, pump head, and tube size. Users of a LabV pump can now easily integrate their pump into some complex workflows involving multiple devices, enhancing operational efficiency and flexibility.
Conclusion
In this blog post, we’ve introduced the LabV peristaltic pump series and explored the basics of its external control Python using the MODBUS RTU communication protocol. We hope this post serves as a valuable guide for researchers and engineers seeking to employ a LabV peristaltic pump in their microfluidics research and experiments.
🚨 While variations exist between the different series of Innofluid Lab pumps, we emphasize that the communication protocol remains almost unchanged. Whether you’re using the LabF series, LabN series, or any other, we trust that this content will also be beneficial for your future projects.
Stay tuned for more insights, tutorials, and practical applications in our future posts. Until then, happy pumping and coding 💻!
📧 If you have any questions or feedback, please feel free to contact us at support@darwin-microfluidics.com.