1. 初识Modbus:工业通信的“普通话”
如果你接触过工业自动化、物联网设备或者智能硬件,Modbus这个名字你一定不会陌生。它就像是工业设备之间的“普通话”,让不同厂家、不同型号的设备能够顺畅交流。我从2013年开始接触工业自动化项目,第一次用Modbus连接PLC和传感器时,那种“终于通了!”的兴奋感至今记忆犹新。
Modbus诞生于1979年,比很多程序员的年龄都大,但至今仍然活跃在各个工厂、楼宇和物联网项目中。为什么一个40多年前的协议还能这么流行?简单来说就是三个词:简单、开源、稳定。协议规范完全公开,任何厂商都可以免费使用,而且实现起来特别简单,一个单片机就能跑起来。
现在很多智能硬件都支持Modbus协议,比如智能电表、温湿度传感器、PLC控制器等等。学会用Python操作Modbus,就相当于获得了一把开启物理世界数据的钥匙。你可以用电脑或者树莓派读取传感器数据,控制设备状态,甚至搭建自己的监控系统。
2. Modbus核心概念:主从架构与数据模型
2.1 主从架构就像课堂提问
理解Modbus的主从架构特别重要,我用一个简单的比喻来解释:想象一个教室里的老师和学生。
老师(主站/Master)负责提问,而且每次只能有一个老师说话。学生(从站/Slave)坐在下面听课,只有被老师点名的时候才能回答。每个学生都有个学号(从站地址),老师提问时会说:“请学号1的同学回答一下当前温度是多少?”
在实际的Modbus网络中,主站就是你的电脑或者控制器,从站就是各种传感器、执行器等设备。主站主动发起请求,从站响应请求,从站之间不能直接通信。这种设计虽然简单,但非常可靠,这也是Modbus能经久不衰的重要原因。
2.2 四种数据类型就像四个抽屉
Modbus设备把数据存放在四个不同的区域,就像一个有四个抽屉的柜子:
| 数据类型 | 地址范围 | 读写权限 | 数据大小 | 典型用途 |
|---|---|---|---|---|
| 线圈(Coils) | 00001-09999 | 读写 | 1位 | 控制继电器、开关 |
| 离散输入(Discrete Inputs) | 10001-19999 | 只读 | 1位 | 读取按钮状态、门磁信号 |
| 输入寄存器(Input Registers) | 30001-39999 | 只读 | 16位 | 读取传感器数据、模拟量输入 |
| 保持寄存器(Holding Registers) | 40001-49999 | 读写 | 16位 | 参数设置、数据存储 |
最常用的是保持寄存器,因为既能读又能写。很多设备厂商为了省事,把所有重要数据都放在这个区域。
这里有个容易踩坑的地方:设备手册上写的地址是40001,但在代码里你要用0。因为Modbus协议内部使用的是从0开始的地址,而人们习惯从1开始计数。所以40001对应地址0,40002对应地址1,以此类推。
3. Modbus的两种传输模式:RTU与TCP
3.1 Modbus RTU:经典的串行通信
Modbus RTU是基于串口的通信方式,通常使用RS-485或者RS-232接口。RS-485特别适合工业环境,支持长距离传输(最长1200米)和多个设备并联。
RTU模式使用二进制格式传输数据,效率很高。一个完整的RTU帧包括从站地址、功能码、数据和CRC校验码。CRC校验很重要,能确保数据在传输过程中没有出错。
我刚开始用RTU模式时,在接线和参数配置上踩过不少坑。RS-485有A、B两根线,接反了通信就会失败。还有就是通信参数必须完全匹配:波特率、数据位、停止位、校验位都要和设备设置一致。常见的配置是9600波特率、8数据位、无校验、1停止位。
3.2 Modbus TCP:现代的网络通信
Modbus TCP是在以太网上运行的Modbus协议,越来越受欢迎。它不需要额外的校验机制,因为TCP协议本身就能保证数据的可靠性。
Modbus TCP在RTU协议的基础上增加了MBAP报文头,包含了事务标识符、协议标识符、长度和单元标识符。单元标识符就相当于RTU模式中的从站地址。
用Modbus TCP最大的好处是方便,直接用网线连接就行,不用折腾串口线和USB转串口工具。而且支持更远的传输距离,只要网络能通,设备在哪都能访问。
4. 环境准备与pymodbus库安装
4.1 安装Python和必要库
咱们先从环境准备开始。我推荐使用Python 3.8或更高版本,太老的版本可能会有兼容性问题。
打开命令行,安装pymodbus库:
pip install pymodbus
如果你要用Modbus RTU(串口通信),还需要安装pyserial:
pip install pyserial
我习惯用virtualenv创建虚拟环境,这样不同项目的依赖不会互相干扰:
python -m venv modbus-env
source modbus-env/bin/activate # Linux/Mac
modbus-env\Scripts\activate # Windows
pip install pymodbus pyserial
4.2 测试环境是否正常
安装完成后,可以写个简单的测试脚本验证环境:
# test_installation.py
from pymodbus.client import ModbusTcpClient
from pymodbus.client import ModbusSerialClient
import serial
print("pymodbus 库安装成功!")
print("可以开始Modbus编程了")
如果运行这个脚本没有报错,说明环境配置成功了。
5. Modbus TCP实战:连接与数据读写
5.1 建立TCP连接
咱们先从Modbus TCP开始,因为这个最简单,不需要额外的硬件。如果你没有实际的Modbus设备,可以用Modbus模拟器来练习。我推荐Modbus Poll或者pyModbusTCP自带的模拟器。
下面是一个完整的Modbus TCP读取示例:
from pymodbus.client import ModbusTcpClient
from pymodbus.exceptions import ModbusException
import logging
# 设置日志,方便调试
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.INFO)
def read_modbus_tcp():
# 连接参数
SERVER_IP = '192.168.1.100' # 替换为你的设备IP
SERVER_PORT = 502
SLAVE_ID = 1
START_ADDRESS = 0 # 对应40001
REGISTER_COUNT = 5
# 创建客户端实例
client = ModbusTcpClient(SERVER_IP, port=SERVER_PORT)
try:
print(f"正在连接 {SERVER_IP}:{SERVER_PORT}...")
if not client.connect():
print("连接失败,请检查网络和IP地址")
return
print("连接成功!")
# 读取保持寄存器
print(f"读取从站{SLAVE_ID}的寄存器...")
result = client.read_holding_registers(
address=START_ADDRESS,
count=REGISTER_COUNT,
slave=SLAVE_ID
)
if result.isError():
print(f"读取错误: {result}")
else:
registers = result.registers
print(f"读取成功: {registers}")
# 显示每个寄存器的值和对应地址
for i, value in enumerate(registers):
print(f"寄存器 {40001 + i} (地址 {START_ADDRESS + i}): {value}")
except ModbusException as e:
print(f"Modbus异常: {e}")
except Exception as e:
print(f"其他异常: {e}")
finally:
client.close()
print("连接已关闭")
if __name__ == "__main__":
read_modbus_tcp()

1万+

被折叠的 条评论
为什么被折叠?



