杰瑞科技汇

Python Modbus TCP如何实现通信?

Of course! Here is a comprehensive guide to using Modbus TCP in Python, covering the most popular library, pymodbus, with practical examples and best practices.

What is Modbus TCP?

Modbus is a serial communication protocol developed in 1979 by Modicon. It's a simple, robust, and widely open protocol, making it a de facto standard in industrial automation.

Modbus TCP is the version of Modbus that runs on top of the TCP/IP protocol suite. Instead of using serial ports (RS-232, RS-485), it uses Ethernet. This makes it faster, easier to integrate with modern IT networks, and allows for communication over much longer distances.

Key concepts:

  • Client/Server Model: A Modbus network has one server (or "slave") and one or more clients (or "masters").
  • Server (Slave): The device that holds the data (e.g., a PLC, a sensor, a VFD drive). It waits for requests from clients and responds with data or an acknowledgment.
  • Client (Master): The device that requests data from the server (e.g., a SCADA system, a data logger, a Python script). It initiates all communication.

The Recommended Library: pymodbus

For Python, the go-to library for Modbus is pymodbus. It's feature-rich, well-maintained, and supports all major Modbus variants, including TCP, RTU (serial), and ASCII.

Installation

First, you need to install the library using pip:

pip install pymodbus

Example 1: Creating a Modbus TCP Server (Slave)

This is a simple example of a device that holds some data and can be queried by a client. We'll create a server that holds a few integer values in its "holding registers".

# server.py
from pymodbus.server import StartTcpServer
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer
# --- 1. Create the Data Store ---
# A datastore holds the actual data the server will manage.
# We'll create a block of 10 holding registers, initialized to 0.
# The addresses will be 0 to 9.
holding_registers = ModbusSequentialDataBlock(0, [0] * 10)
# A slave context maps the different types of registers (holding, input, etc.)
# to our data blocks. Here, we only define holding registers.
slave_context = ModbusSlaveContext(hr=holding_registers)
# A server context can manage multiple slaves. Here we have just one slave with ID 1.
# The context is a dictionary: {slave_id: slave_context}
server_context = ModbusServerContext(slaves={1: slave_context}, single=True)
# --- 2. Start the Server ---
print("Starting Modbus TCP Server...")
# The server will listen on all network interfaces ('0.0.0.0') on port 502.
# This is the standard port for Modbus TCP.
try:
    StartTcpServer(
        context=server_context,
        address=("0.0.0.0", 502), # Listen on all interfaces, port 502
        # framer=ModbusRtuFramer # Use this for RTU/Serial, not for TCP
    )
except KeyboardInterrupt:
    print("\nServer stopped.")

How to Run:

  1. Save the code as server.py.
  2. Run it from your terminal: python server.py.
  3. The server will now be running in the background, waiting for connections. You won't see much output, but it's active. You can stop it with Ctrl+C.

Example 2: Creating a Modbus TCP Client (Master)

Now, let's create a Python script that acts as a client to read data from the server we just started.

# client.py
from pymodbus.client import ModbusTcpClient
import time
# --- 1. Define Server Connection Details ---
# Replace '127.0.0.1' with the IP address of your Modbus server if it's remote.
SERVER_IP = "127.0.0.1"
SERVER_PORT = 502
SLAVE_ID = 1  # The ID of the slave device we want to communicate with
# --- 2. Create the Client ---
# The client object handles the connection.
print(f"Connecting to Modbus server at {SERVER_IP}:{SERVER_PORT}...")
client = ModbusTcpClient(SERVER_IP, port=SERVER_PORT)
client.connect()
if client.connected:
    print("Successfully connected!")
    # --- 3. Read Holding Registers ---
    # We want to read 5 registers starting from address 0.
    # The function code for reading holding registers is 0x03.
    try:
        # Read 5 registers from address 0
        result = client.read_holding_registers(address=0, count=5, slave=SLAVE_ID)
        # --- 4. Process the Response ---
        if not result.isError():
            print("\n--- Read Successful ---")
            print(f"Read {len(result.registers)} registers starting at address 0:")
            print(f"Values: {result.registers}")
            # Let's write a new value to register 0
            new_value = 123
            print(f"\n--- Writing {new_value} to register 0 ---")
            write_result = client.write_register(address=0, value=new_value, slave=SLAVE_ID)
            if not write_result.isError():
                print("Write successful!")
                # Read again to confirm the write
                read_after_write = client.read_holding_registers(address=0, count=1, slave=SLAVE_ID)
                if not read_after_write.isError():
                    print(f"Value of register 0 after write: {read_after_write.registers[0]}")
            else:
                print(f"Write failed: {write_result}")
        else:
            print(f"Read failed: {result}")
    except Exception as e:
        print(f"An error occurred: {e}")
    finally:
        # --- 5. Close the Connection ---
        print("\nClosing connection.")
        client.close()
else:
    print("Failed to connect to the server.")

How to Run:

  1. Make sure your server.py is still running.
  2. Save the client code as client.py.
  3. Run it from another terminal: python client.py.

Expected Output:

Connecting to Modbus server at 127.0.0.1:502...
Successfully connected!
--- Read Successful ---
Read 5 registers starting at address 0:
Values: [0, 0, 0, 0, 0]
--- Writing 123 to register 0 ---
Write successful!
Value of register 0 after write: 123
Closing connection.

If you run the client again immediately, you should see the value 123 instead of 0.


Understanding the Modbus Data Model

It's crucial to understand the four main data areas in Modbus. The function code you use determines which area you access.

Area Name Abbrev. Function Code Access Typical Use
Coils - read_coils / write_coil Read/Write On/Off outputs (e.g., relay, lamp). Boolean values.
Discrete Inputs di read_discrete_inputs Read-Only On/Off inputs from sensors (e.g., limit switch). Boolean values.
Input Registers ir read_input_registers Read-Only Data from sensors (e.g., temperature, pressure). 16-bit values.
Holding Registers hr read_holding_registers / write_register Read/Write Configuration parameters, counters, other data. 16-bit values.

Client Methods for Different Data Areas

  • Coils:
    • client.read_coils(address, count, slave)
    • client.write_coil(address, value, slave) (value is True/False)
    • client.write_coils(address, values, slave) (values is a list of True/False)
  • Discrete Inputs:
    • client.read_discrete_inputs(address, count, slave)
  • Input Registers:
    • client.read_input_registers(address, count, slave)
  • Holding Registers:
    • client.read_holding_registers(address, count, slave)
    • client.write_register(address, value, slave) (value is an integer)
    • client.write_registers(address, values, slave) (values is a list of integers)

Best Practices and Troubleshooting

  1. Error Handling: Always check the result of a Modbus call using result.isError(). This will catch protocol-level errors (e.g., invalid slave ID, illegal function code).
  2. Connection Management: It's good practice to connect, perform your operations, and then disconnect. For long-running applications, you might keep the connection open but be aware of network issues.
  3. Slave ID: The slave parameter is critical. If you get "No response from slave" errors, the first thing to check is if you are using the correct slave ID for your device.
  4. IP Address and Port: Ensure the client is trying to connect to the correct IP address and port (502 is standard). Firewalls can block this port.
  5. Data Types: Modbus registers are 16-bit. To handle 32-bit integers or floating-point numbers, you need to combine two registers (register n and n+1). The order (endianness) is important and depends on the device's implementation. pymodbus has helper functions for this, like decode_32bit_int and decode_32bit_float.
  6. Tools for Debugging:
    • Modbus Poll / Modbus Scanner: Excellent commercial tools for testing and querying Modbus devices. They are invaluable for verifying that a device is working before you even write a line of Python code.
    • Wireshark: The ultimate network protocol analyzer. You can use it to see exactly what Modbus TCP packets are being sent and received on your network, helping you diagnose low-level communication problems.
分享:
扫描分享到社交APP
上一篇
下一篇