一文读懂 Modbus 协议:原理、代码示例与应用

在工业自动化领域,设备之间的通信是实现高效生产的关键。而 Modbus 协议作为工业通信领域的“老大哥”,自 1979 年诞生以来,一直扮演着至关重要的角色。今天,就让我们深入探索 Modbus 协议的奥秘,从原理到代码示例,全方位了解它如何让工业设备“对话”。

一、Modbus 协议简介

(一)协议的起源与地位

Modbus 协议最初是为了解决工业环境中设备之间的通信问题而设计的。它是一种应用层协议,位于 OSI 模型的第 7 层,主要在客户机/服务器架构下工作。无论是串行链路(如 RS-232 和 RS-485)还是基于 TCP/IP 的以太网,Modbus 协议都能大显身手。

(二)协议的两大分支

Modbus 协议主要分为两大类:串行链路上的 Modbus 和 TCP/IP 上的 Modbus。串行链路的 Modbus 依赖于 TIA/EIA 标准,而 Modbus TCP/IP 则基于 IETF 的 RFC793 和 RFC791 标准。这种灵活性使得 Modbus 协议能够适应不同的网络环境,满足各种工业场景的需求。

二、Modbus 协议格式详解

(一)协议数据单元(PDU)

Modbus 协议的核心是协议数据单元(PDU),它是一个与基础通信层无关的简单数据结构。PDU 包括功能码和数据两部分。功能码告诉服务器需要执行什么操作,而数据则是操作的具体内容。

(二)应用数据单元(ADU)

在实际通信中,Modbus 协议使用应用数据单元(ADU)。ADU 在 PDU 的基础上增加了地址域和差错校验域。地址域用于标识子节点,差错校验则确保数据传输的准确性。

(三)帧结构

一个完整的 Modbus 帧包括起始帧、数据帧和校验帧。起始帧标志着数据传输的开始,数据帧包含了实际的通信内容,而校验帧用于验证数据的完整性。

(四)功能码与异常响应

功能码是 Modbus 协议中的关键部分,它定义了服务器需要执行的操作。如果操作成功,服务器会返回一个与请求功能码相同的响应;如果出现错误,则返回一个异常响应,异常响应的功能码是请求功能码加上 0x80。

三、Modbus 数据模型

Modbus 协议的数据模型基于四个基本表格:线圈状态表、离散输入状态表、保持寄存器表和输入寄存器表。每个表格都可以存储 65536 个数据项,这些数据项可以通过不同的功能码进行读写操作。

(一)线圈状态表

线圈状态表用于存储布尔值(0 或 1),通常用于控制设备的开关状态。例如,一个简单的应用场景是通过 Modbus 协议控制一个电机的启动和停止。

(二)离散输入状态表

离散输入状态表用于读取设备的输入状态,这些状态通常是只读的。例如,它可以用来读取传感器的输入信号。

(三)保持寄存器表

保持寄存器表用于存储 16 位的整数值,这些值可以被读取和写入。保持寄存器表常用于存储设备的配置参数或运行状态。

(四)输入寄存器表

输入寄存器表与保持寄存器表类似,但它只能被读取,不能被写入。它通常用于存储设备的实时数据。

四、Modbus 协议的代码示例

(一)Modbus TCP 示例

以下是一个使用 Python 和 pymodbus 库实现的 Modbus TCP 客户端和服务器的简单示例。

1. 安装 pymodbus 库
pip install pymodbus
2. Modbus TCP 服务器代码
from pymodbus.server.sync import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

def run_server():
    # 创建数据存储
    store = ModbusSlaveContext(
        di=ModbusSequentialDataBlock(0, [0]*100),  # 离散输入
        co=ModbusSequentialDataBlock(0, [0]*100),  # 线圈
        hr=ModbusSequentialDataBlock(0, [0]*100),  # 保持寄存器
        ir=ModbusSequentialDataBlock(0, [0]*100))  # 输入寄存器
    context = ModbusServerContext(slaves={1: store}, single=True)

    # 设置设备信息
    identity = ModbusDeviceIdentification()
    identity.VendorName = 'pymodbus'
    identity.ProductCode = 'PM'
    identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
    identity.ProductName = 'pymodbus Server'
    identity.ModelName = 'pymodbus Server'
    identity.MajorMinorRevision = '1.5'

    # 启动服务器
    StartTcpServer(context, identity=identity, address=("localhost", 502))

if __name__ == "__main__":
    run_server()
3. Modbus TCP 客户端代码
from pymodbus.client.sync import ModbusTcpClient as ModbusClient

def run_client():
    client = ModbusClient('localhost', port=502)
    client.connect()

    # 读取保持寄存器
    rr = client.read_holding_registers(1, 1, unit=1)
    print("保持寄存器值:", rr.registers)

    # 写入保持寄存器
    rq = client.write_register(1, 10, unit=1)
    print("写入保持寄存器:", rq)

    # 读取线圈状态
    rr = client.read_coils(1, 1, unit=1)
    print("线圈状态:", rr.bits)

    client.close()

if __name__ == "__main__":
    run_client()

(二)Modbus RTU 示例

Modbus RTU 是一种基于串行链路的通信方式,以下是一个使用 Python 和 pyserial 库实现的 Modbus RTU 客户端示例。

1. 安装 pyserial 库
pip install pyserial
2. Modbus RTU 客户端代码
import serial
import struct

def calculate_crc(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

def send_modbus_request(port, address, function_code, data):
    request = struct.pack('>B', address) + struct.pack('>B', function_code) + data
    crc = calculate_crc(request)
    request += struct.pack('>H', crc)
    port.write(request)

def read_modbus_response(port):
    response = port.read(1024)
    return response

def run_client():
    port = serial.Serial('/dev/ttyUSB0', baudrate=9600, timeout=1)
    address = 1
    function_code = 3  # 读取保持寄存器
    start_address = 0x0001
    quantity = 1

    data = struct.pack('>H', start_address) + struct.pack('>H', quantity)
    send_modbus_request(port, address, function_code, data)
    response = read_modbus_response(port)
    print("响应数据:", response)

if __name__ == "__main__":
    run_client()

五、Modbus 协议的应用场景

(一)工业自动化控制

Modbus 协议在工业自动化领域有着广泛的应用。例如,通过 Modbus 协议,可以实现对工厂生产线上设备的远程监控和控制。操作人员可以在控制中心通过 Modbus 客户端软件发送指令,控制生产线上的电机、阀门等设备。

(二)能源管理系统

在能源管理系统中,Modbus 协议用于采集和传输设备的能耗数据。例如,通过 Modbus 协议连接的智能电表可以将实时的电能数据传输到能源管理服务器,服务器可以根据这些数据进行能耗分析和优化。

(三)智能建筑

在智能建筑中,Modbus 协议用于连接各种设备,如空调、照明、电梯等。通过 Modbus 协议,可以实现对这些设备的集中控制和管理,提高建筑的能源利用效率和舒适度。

六、总结

Modbus 协议作为工业通信领域的经典协议,凭借其简单、灵活和高效的特点,一直受到广泛应用。无论是串行链路还是 TCP/IP 网络,Modbus 协议都能提供可靠的通信解决方案。通过本文的介绍,相信你对 Modbus 协议的原理、数据模型和代码实现有了更深入的理解。在实际应用中,掌握 Modbus 协议可以帮助你更好地实现工业设备之间的互联互通,提升工业自动化的水平。

如果你对 Modbus 协议还有其他疑问,或者想了解更多相关的技术细节,欢迎在评论区留言,我们一起探讨!

你可能感兴趣的:(python)