
qt5-serialbus是 Qt5 用于访问各类工业现场总线和协议的附加模块自 Qt 5.8 起正式引入内部包含ModbusTCP/RTU与CAN Bus两大子系统覆盖了 PLC 通信、仪表采集、汽车电子总线等工业场景。它不在qt5-base中需通过 vcpkg 单独安装。模块整体定位与两大子系统维度Qt Modbus 子系统Qt CAN Bus 子系统协议Modbus TCP / Modbus RTUCAN 2.0A/B、CAN FD角色主站Client/ 从站Server总线节点收发帧传输介质以太网502端口/ RS-232/485 串口各类 CAN 适配器USB-PCIe核心基类QModbusDeviceQCanBusDevice工厂类直接 new 具体子类QCanBus::instance()-createDevice()数据单元QModbusDataUnit4 类寄存器QCanBusFrameID payload异步句柄QModbusReplyframesReceived信号 readFrame()典型场景上位机与 PLC、仪表通信车辆 ECU、工业控制器总线类层级结构下面用一张类图展示整个模块的继承关系这是理解 API 设计的关键CAN子系统Modbus子系统QObjectQModbusDevice连接/错误/状态/参数QModbusClientsendRead/Write/RawRequestQModbusServersetMap/setData/dataWrittenQModbusTcpClientQModbusRtuSerialMasterQModbusTcpServerQModbusRtuSerialSlaveQModbusReply异步响应句柄QModbusDataUnit寄存器类型地址值QCanBus工厂插件枚举QCanBusDeviceconnectDevice/writeFrameQCanBusFrameframeIdpayloadQCanBusDeviceInfoModbus 侧QModbusDevice是抽象基类提供连接状态state()、错误处理error()/errorString()、连接参数setConnectionParameter()等通用能力QModbusClient与QModbusServer分别派生为主站和从站抽象再按 TCP/RTU 细化为 4 个具体类。CAN 侧QCanBus是单例工厂通过插件机制加载不同厂商后端QCanBusDevice封装具体设备的连接、配置、收发QCanBusFrame表示一个 CAN 帧。四种 Modbus 寄存器类型Modbus 协议规定每个从站设备维护 4 张数据表QModbusDataUnit::RegisterType枚举与之对应寄存器类型枚举值数据宽度读写权限功能码典型用途离散输入 Discrete Inputs11 bit只读02按钮、开关状态线圈 Coils01 bit读写01/05/15继电器输出、指示灯输入寄存器 Input Registers316 bit只读04ADC 采样值、传感器读数保持寄存器 Holding Registers216 bit读写03/06/16设定参数、计数器读写权限决定了主站能用哪些功能码访问只读表只能用读功能码读写表既能读也能写。主站核心 APIQModbusClientQModbusClient提供 4 个异步请求方法返回QModbusReply*句柄方法功能对应功能码sendReadRequest(unit, serverAddress)读寄存器01/02/03/04sendWriteRequest(unit, serverAddress)写寄存器05/06/15/16sendReadWriteRequest(read, write, addr)原子读写23sendRawRequest(request, addr)发送原始 PDU任意配置方法setNumberOfRetries(n)默认 3 次重试、setTimeout(ms)默认 1000ms。从站核心 APIQModbusServerQModbusServer维护内存中的寄存器映射表核心方法setMap(QModbusDataUnitMap)—— 初始化 4 张表的大小与起始地址setData(table, address, value)—— 写入单个寄存器程序内部更新data(table, address, data)—— 读取单个寄存器setServerAddress(int)—— 设置从站地址1~247信号dataWritten(table, address, size)—— 主站写入时触发用于业务联动从站 setMap 初始化寄存器映射示例从站必须先调用setMap()划分 4 张表的地址空间主站才能读写// 创建 TCP 从站QModbusTcpServer*servernewQModbusTcpServer(this);server-setConnectionParameter(QModbusDevice::NetworkAddressParameter,0.0.0.0);server-setConnectionParameter(QModbusDevice::NetworkPortParameter,502);server-setServerAddress(1);// 初始化 4 张数据表各 100 个条目起始地址 0QModbusDataUnitMap reg;reg.insert(QModbusDataUnit::DiscreteInputs,{QModbusDataUnit::DiscreteInputs,0,100});reg.insert(QModbusDataUnit::Coils,{QModbusDataUnit::Coils,0,100});reg.insert(QModbusDataUnit::InputRegisters,{QModbusDataUnit::InputRegisters,0,100});reg.insert(QModbusDataUnit::HoldingRegisters,{QModbusDataUnit::HoldingRegisters,0,100});server-setMap(reg);// 设置初始值server-setData(QModbusDataUnit::HoldingRegisters,0,1234);// 监听主站写入connect(server,QModbusServer::dataWritten,[](QModbusDataUnit::RegisterType table,intaddress,intsize){qDebug()主站写入:tableaddraddresssizesize;});server-connectDevice();RTU 主站完整示例读保持寄存器这是工业上位机最典型的用法配置串口 → 连接 → 异步读 → 在finished信号中取结果。#includeQModbusRtuSerialMaster#includeQModbusDataUnit#includeQModbusReply#includeQSerialPort#includeQDebug// 1. 创建 RTU 主站QModbusRtuSerialMaster*clientnewQModbusRtuSerialMaster(this);// 2. 配置串口参数client-setConnectionParameter(QModbusDevice::SerialPortNameParameter,COM3);client-setConnectionParameter(QModbusDevice::SerialBaudRateParameter,QSerialPort::Baud9600);client-setConnectionParameter(QModbusDevice::SerialDataBitsParameter,QSerialPort::Data8);client-setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::NoParity);client-setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::OneStop);client-setTimeout(1000);client-setNumberOfRetries(3);// 3. 连接if(!client-connectDevice()){qDebug()连接失败:client-errorString();return;}// 4. 读保持寄存器从站地址1起始地址0数量10QModbusDataUnitreadUnit(QModbusDataUnit::HoldingRegisters,0,10);if(auto*replyclient-sendReadRequest(readUnit,1)){if(!reply-isFinished()){connect(reply,QModbusReply::finished,[reply](){if(reply-error()QModbusDevice::NoError){constQModbusDataUnit unitreply-result();for(inti0;iunit.valueCount();i)qDebug()Reg[unit.startAddress()i] unit.values().at(i);}else{qDebug()读错误:reply-errorString();}reply-deleteLater();});}else{deletereply;// 同步完成广播请求}}TCP 主站只需替换两行把 RTU 示例中的创建与配置换成 TCP 版本其余sendReadRequest/finished逻辑完全一致QModbusTcpClient*clientnewQModbusTcpClient(this);client-setConnectionParameter(QModbusDevice::NetworkAddressParameter,192.168.1.100);client-setConnectionParameter(QModbusDevice::NetworkPortParameter,502);client-connectDevice();写寄存器示例// 写单个保持寄存器地址 5 写入 0x1234QModbusDataUnitwriteUnit(QModbusDataUnit::HoldingRegisters,5,QVectorquint16{0x1234});if(auto*replyclient-sendWriteRequest(writeUnit,1)){if(!reply-isFinished()){connect(reply,QModbusReply::finished,[reply](){if(reply-error()QModbusDevice::NoError)qDebug()写入成功;elseqDebug()写入失败:reply-errorString();reply-deleteLater();});}else{deletereply;}}CAN Bus 子系统快速上手CAN 侧采用插件机制通过QCanBus工厂创建设备#includeQCanBus#includeQCanBusFrame#includeQDebug// 1. 枚举可用插件qDebug()可用插件:QCanBus::instance()-plugins();// 2. 创建设备Linux 用 socketcanWindows 用 peakcanQString err;QCanBusDevice*deviceQCanBus::instance()-createDevice(socketcan,can0,err);if(!device){qWarning()创建失败:err;return;}// 3. 配置波特率device-setConfigurationParameter(QCanBusDevice::BitRateKey,250000);// 4. 连接接收信号connect(device,QCanBusDevice::framesReceived,[device](){while(device-framesAvailable()){QCanBusFrame framedevice-readFrame();qDebug()ID:QString::number(frame.frameId(),16)Data:frame.payload().toHex( );}});// 5. 连接并发送device-connectDevice();QCanBusFrame txFrame;txFrame.setFrameId(0x123);txFrame.setPayload(QByteArray::fromHex(0102030405));device-writeFrame(txFrame);支持的 CAN 插件列表Qt SerialBus 通过插件支持主流 CAN 硬件厂商插件 key厂商平台socketcanLinux 内核 SocketCANLinuxpassthrucanSAE J2534 Pass-ThruWindowspeakcanPEAK-System (PCAN)全平台vectorcanVector InformatikWindowssysteccanSYS TEC electronic全平台tinycanMHS Elektronik全平台virtualcan虚拟 CANTCP/IP全平台测试用完整工程封装建议一个健壮的 Modbus 通信层应分层封装QModbusReply::finishedQModbusServer::dataWritten配置层 ModbusConfig串口/IP/波特率/超时通信层 ModbusManagerconnectDevice/sendReadRequest协议层 ModbusProtocol帧解析/重试/队列应用层 BusinessLogicsetData/读结果更新UI配置层把所有连接参数封装为结构体支持从配置文件或 UI 读取通信层封装QModbusClient生命周期对外暴露readRegisters()/writeRegisters()异步接口协议层处理重试、超时、请求队列、字节序转换应用层响应finished或dataWritten信号更新 UI 或执行业务常见问题与排查① RTU 通信超时TimeoutError默认超时 1000ms部分仪表响应慢需调大RTU 还有setTurnaroundDelay()周转延迟参数控制主站发完请求到从站开始响应的最短间隔设备响应慢时需调大。② 串口参数不匹配导致乱码波特率、校验位、数据位、停止位必须与从站完全一致。工业仪表常见 9600/8/N/1 或 19200/8/E/1需查设备手册。参数不一致时主站会收到ResourceError或乱码。③ TCP 连接被拒检查目标 IP 是否可达、502 端口是否开放、防火墙是否放行部分 PLC 需在工程中显式启用 Modbus TCP Server 功能。④ 32 位浮点数字节序问题Modbus 寄存器是 16 位存储 32 位 float/long 时存在大端/小端和字交换 4 种组合。Qt 不自动转换需用QByteArrayQDataStream手动处理或按设备手册的字节序拼接两个quint16。⑤ QModbusReply 内存泄漏sendReadRequest返回的reply必须在finished槽中调用deleteLater()否则内存泄漏若是广播请求serverAddress0isFinished()立即为 true需手动delete。⑥ RTU 帧间隔不足Modbus RTU 协议要求帧间至少 3.5 个字符时间的空闲。高波特率下若主站连续发送过快从站可能无法识别帧边界。可通过setInterFrameDelay()调整。⑦ 开启 Modbus 调试日志在main()中添加以下代码可看到完整的请求/响应报文排查问题极为方便QLoggingCategory::setFilterRules(QStringLiteral(qt.modbus* true));若要查看更底层的字节内容使用qt.modbus.lowlevel true。选型建议与 PLC/仪表通信选 Modbus 子系统以太网环境用QModbusTcpClient串口环境用QModbusRtuSerialMaster模拟从站供主站调试用QModbusTcpServer或QModbusRtuSerialSlave配合setMap()建数据表汽车电子/ECU 通信选 CAN 子系统Linux 用socketcanWindows 用peakcan或vectorcan无硬件开发调试CAN 用virtualcan插件Modbus 用虚拟串口 Modbus Poll/Slave 工具非 Qt 项目或需更轻量可考虑libmodbus纯 C 库作为替代方案