QModbusTcpClient需显式设置IP、端口、超时和重试参数,连接后监听stateChanged信号验证会话,读寄存器地址须按PLC厂商映射规则偏移(如4x00001可能对应地址0或1),32位数据须手动处理大端/小端字节序。

直接用 QModbusTcpClient 就能通,但光连上不等于能读对写准——施耐德、西门子、信捷这些PLC对地址映射、字节序、功能码响应的处理各不相同,硬套默认逻辑大概率拿到乱码或超时失败。
QModbusTcpClient 初始化和连接参数必须显式设全
QModbusTcpClient 不会自动填默认值,漏设任何一项都可能静默失败(比如没设超时,sendReadRequest 卡死在阻塞等待)
常见遗漏项:setConnectionParameter(QModbusDevice::NetworkAddressParameter, "192.168.1.10")、setConnectionParameter(QModbusDevice::NetworkPortParameter, 502)、setTimeout(1000)、setNumberOfRetries(2)
注意:IP 地址不能带端口(如 "192.168.1.10:502" 是错的),端口必须单独用 NetworkPortParameter 设置
连接后务必监听 stateChanged 信号,仅靠 connectToDevice() 返回值不可靠——它只表示发起连接,不表示已建立 Modbus 会话
读保持寄存器时地址要减1,且注意PLC厂商的起始偏移
Modbus 协议规范里“4x00001”这种地址是给人看的,实际报文用的是从 0 开始的索引。但不同 PLC 对“4x”区的映射起点不同:
- 施耐德 M340/Quantum 默认从 4x00001 → 寄存器地址 0(即
QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 0, 1)) - 西门子 S7-1200 常见配置是 4x00001 → 寄存器地址 0,但部分项目手动偏移为 +1(即 4x00001 对应地址 1)
- 信捷 XD 系列有些固件把 4x00001 映射到地址 1,导致你读地址 0 返回非法地址异常(
0x02)
实操建议:先用 Modbus Poll 工具连同一台 PLC,读地址 0 和 1,看哪个返回有效数据;再在 Qt 代码里同步调整 QModbusDataUnit 的起始地址
32位数据读写必须手动处理字节序,QModbusTcpClient 不做转换
QModbusDataUnit 每次读写只操作 quint16,32位整数或 float 要跨两个寄存器。Qt 本机是小端,而多数 PLC(包括施耐德)默认大端存储:
假设你要读 4x00010 开始的 32 位有符号整数:
- 先发请求读 2 个寄存器:
QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 9, 2)(因 4x00010 对应地址 9) - 收到
QVector后,若 PLC 是大端(高位在前):quint32 value = (data[0] - 若 PLC 是小端(低位在前):
quint32 value = (data[1]
别猜,用 Wireshark 抓一次 Modbus Poll 的通信包,看 PDU 数据区两个寄存器字节的实际顺序,再对应写转换逻辑
写多个寄存器失败?检查数据长度和PLC写保护
modbus_write_registers 或 QModbusTcpClient::sendWriteRequest 写多寄存器时,常见失败原因不是代码问题:
- 施耐德 PLC 的某块保持寄存器区域可能被设为只读(即使功能码是 0x10),需进 SoMove 或 EcoStruxure 查看该地址段属性
- 一次性写超过 123 个寄存器(Modbus TCP 标准上限)会触发
QModbusResponse::IllegalDataValue错误 - 写入数据长度必须严格匹配请求长度——
QModbusDataUnit构造时指定 10 个寄存器,你就得传QVector长度为 10,少一个都会被 PLC 拒绝
调试时优先用单寄存器写(writeSingleRegister)验证通路,再扩到批量,避免把地址错、权限错、长度错混在一起排查
真正卡住人的从来不是“怎么连”,而是“连上之后为什么值不对”。地址偏移、字节序、寄存器权限这三处,每个都得拿真实 PLC+抓包工具一一对齐,没法靠文档或经验直接套用。











