You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Raspberry Pi 3基于Python实现Modbus TCP服务器向SCADA传输传感器数据

嘿,刚好有过类似的项目经验,给你梳理一个清晰的实现方案——在树莓派3上用Python搭建Modbus TCP服务器,把传感器数据同步到保持寄存器供SCADA读取,步骤很明确:

第一步:选对工具——安装pymodbus库

pymodbus是Python生态里最成熟的Modbus实现库,支持TCP/RTU等多种模式,完全适配你的需求。在树莓派终端直接执行安装命令:

pip install pymodbus
第二步:搭建Modbus TCP服务器核心框架

核心逻辑是:初始化一块保持寄存器区域,让服务器监听Modbus默认的502端口,同时把从Arduino获取的传感器数据实时更新到这些寄存器里。

给你一个可直接复用的示例代码,我会逐段解释关键部分:

from pymodbus.server import StartTcpServer
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext
import threading
import time

# 1. 初始化保持寄存器:这里定义10个寄存器(地址0-9),初始值设为0
# 你可以根据传感器数量调整寄存器数量,比如浮点型传感器需要占2个寄存器
holding_registers = ModbusSequentialDataBlock(0, [0]*10)

# 2. 创建从机上下文,只绑定我们需要的保持寄存器
slave_context = ModbusSlaveContext(
    di=None,  # 不需要离散输入寄存器
    co=None,  # 不需要线圈寄存器
    hr=holding_registers,  # 关联保持寄存器存储块
    ir=None   # 不需要输入寄存器
)

# 3. 创建服务器上下文,使用单从机模式即可
server_context = ModbusServerContext(slaves=slave_context, single=True)

# 4. 数据更新函数:替换成你从Arduino读取传感器数据的逻辑
def update_sensor_data():
    while True:
        # 这里模拟你已经获取到的传感器数据,实际替换成你的读取代码
        temp = 25.8  # 温度示例
        humidity = 61.2  # 湿度示例
        pressure = 1012.7  # 气压示例

        # 处理浮点型数据:Modbus保持寄存器是16位整数,浮点需要拆成两个寄存器
        from pymodbus.payload import BinaryPayloadBuilder
        from pymodbus.constants import Endian

        # 温度存到地址0-1(两个寄存器)
        builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big)
        builder.add_32bit_float(temp)
        temp_regs = builder.to_registers()
        holding_registers.setValues(0, temp_regs)

        # 湿度存到地址2-3
        builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big)
        builder.add_32bit_float(humidity)
        hum_regs = builder.to_registers()
        holding_registers.setValues(2, hum_regs)

        # 气压存到地址4-5
        builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big)
        builder.add_32bit_float(pressure)
        press_regs = builder.to_registers()
        holding_registers.setValues(4, press_regs)

        # 按你的采集频率调整更新间隔,这里设为1秒
        time.sleep(1)

if __name__ == "__main__":
    # 用单独线程更新数据,避免阻塞Modbus服务器
    data_thread = threading.Thread(target=update_sensor_data)
    data_thread.daemon = True
    data_thread.start()

    # 启动Modbus TCP服务器,监听所有网卡的502端口
    print("Modbus TCP Server started on 0.0.0.0:502...")
    StartTcpServer(context=server_context, address=("0.0.0.0", 502))
第三步:关键细节说明
  • 寄存器地址规划:SCADA配置时要对应代码里的地址,比如温度对应0-1号寄存器,湿度对应2-3号,以此类推。如果是整数型传感器数据,直接用单个寄存器即可,不用拆分。
  • 线程处理:单独开线程更新数据,保证Modbus服务器不会被数据采集逻辑阻塞,SCADA随时能读取到最新值。
  • 数据类型兼容:用BinaryPayloadBuilder处理浮点型转寄存器的逻辑,注意字节序(一般用Big Endian),要和SCADA端的解析规则保持一致,否则会读取出乱码。
第四步:测试与验证
  1. 运行树莓派上的Python脚本,确认服务器启动成功。
  2. 用Modbus调试工具(比如modpoll或Modbus Poll)连接树莓派IP和502端口,读取保持寄存器验证数据:
    比如用modpoll命令读取温度数据:
    modpoll -t4 -c2 -r1 你的树莓派IP
    
    这里-t4表示读取32位浮点型,-c2表示读2个寄存器,-r1对应代码里的0号地址(部分工具是1-based地址,注意对应)。
注意事项
  • 树莓派要开放502端口,若开启防火墙,执行:sudo ufw allow 502/tcp
  • SCADA端的寄存器地址、数据类型必须和代码定义完全匹配,否则读取数据会异常
  • 如果传感器采集频率很高,可调整time.sleep()的间隔,pymodbus的setValues方法是线程安全的,无需额外加锁

内容的提问来源于stack exchange,提问作者Voicu Mihuţ STĂNESE

火山引擎 最新活动