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端的解析规则保持一致,否则会读取出乱码。
第四步:测试与验证
- 运行树莓派上的Python脚本,确认服务器启动成功。
- 用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




