基于Qt开发的Modbus主站软件

目录

      • 一、开发成果展示
      • 二、环境搭建与依赖配置
        • 1.​​安装 Qt Serial Bus 模块​​
        • 2.硬件准备​​
      • 三、Modbus RTU 主站软件界面设计
        • 1.界面设计
      • 四、Modbus RTU 主站(客户端)开发
        • 1. 初始化连接
        • 2. 数据读写操作
      • 五、扩展功能
      • 六、完整代码示例
        • 1.mainwindow.h
        • 2.mainwindow.cpp
        • 3.main.cpp
      • 七、结语

Modbus作为工业领域广泛应用的通信协议,其主站(Master)开发需满足​​实时性​​、​​稳定性​​与​​跨平台兼容性​​。Qt凭借其​​跨平台特性​​和​​丰富的通信模块​​(如QtSerialPort、QtSerialBus),成为开发Modbus主站软件的理想选择。本文主要讲解使用Qt开发ModbusRtu主站软件。

一、开发成果展示

基于Qt开发的Modbus主站软件_第1张图片

二、环境搭建与依赖配置

1.​​安装 Qt Serial Bus 模块​​

Qt 通过 Qt Serial Bus 模块提供 Modbus RTU 支持,需在 Qt 安装时勾选该模块。在项目配置文件(.pro)中添加依赖:

QT += core gui serialbus serialport
CONFIG += c++17
2.硬件准备​​

确保串口设备(如 USB 转 RS485 适配器)已正确连接,并在代码中指定对应端口(如 COM3 或 /dev/ttyUSB0)或者使用Modbus仿真软件进行模拟测试。

三、Modbus RTU 主站软件界面设计

1.界面设计

基于Qt开发的Modbus主站软件_第2张图片
以上为ModbusRtu主站软件界面设计效果图。

四、Modbus RTU 主站(客户端)开发

1. 初始化连接
m_modbusRtuDevice = new QModbusRtuSerialMaster(this);
    connect(m_modbusRtuDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        ui->textEdit->append(m_modbusRtuDevice->errorString());
    });
    connect(m_modbusRtuDevice,&QModbusClient::stateChanged,[this](QModbusDevice::State state){
        if(state==QModbusDevice::UnconnectedState){
            ui->textEdit->append("connect ModbusTcp Device failed");
        }
        else if(state==QModbusDevice::ConnectedState){
            ui->textEdit->append("connect ModbusTcp Device success!");
        }
        else if(state==QModbusDevice::ClosingState){
            ui->textEdit->append("connect ModbusTcp Device Closing.");
        }
    });
    m_modbusRtuDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                                              m_comName);
    m_modbusRtuDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                                              m_parity);
    m_modbusRtuDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                                              m_baud);
    m_modbusRtuDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                                              m_dataBits);
    m_modbusRtuDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                                              m_stopBits);
    m_modbusRtuDevice->setTimeout(1000);
    m_modbusRtuDevice->setNumberOfRetries(3);
    m_modbusRtuDevice->connectDevice();

此代码配置了串口参数并尝试连接设备,需根据硬件实际情况调整波特率、端口号等。

2. 数据读写操作

​​读取保持寄存器(功能码 0x03)​​:

QString sendFrame = QString("%1%2%3%4").arg(slaveId,2,16,QLatin1Char('0'))
                            .arg(fcode,2,16,QLatin1Char('0'))
                            .arg(startAddress,4,16,QLatin1Char('0'))
                            .arg(readNum,4,16,QLatin1Char('0'));
    QString crc16 = QString("%1").arg(crc16Modbus(QByteArray::fromHex(sendFrame.toUtf8())),4,16,QLatin1Char('0'));
    sendFrame = QString("%1%2").arg(sendFrame,crc16);
    ui->textEdit->append(QString("发送:%1").arg(QString::fromUtf8(QByteArray::fromHex(sendFrame.toUtf8()).toHex(' ').toUpper())));
    QModbusDataUnit read;
    switch (fcode) {
    case 0x01:
        read = QModbusDataUnit(QModbusDataUnit::Coils, startAddress, readNum);//读线圈
        break;
    case 0x02:
        read = QModbusDataUnit(QModbusDataUnit::DiscreteInputs, startAddress, readNum);//读离散量输入
        break;
    case 0x03:
        read = QModbusDataUnit(QModbusDataUnit::HoldingRegisters, startAddress, readNum);//读保持寄存器
        break;
    case 0x04:
        read = QModbusDataUnit(QModbusDataUnit::InputRegisters, startAddress, readNum);//读输入寄存器
        break;
    default:
        ui->textEdit->append("read frame error.");
        return;
        break;
    }
    if (auto *reply = m_modbusRtuDevice->sendReadRequest(read, slaveId)) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &MainWindow::onReadModbus);
        else{
            delete reply;
            connectModbus();
        }
    }
    else{
        ui->textEdit->append("Read error: "+m_modbusRtuDevice->errorString());
        connectModbus();
    }

写入单个线圈(功能码 0x06)​​:

	if (!m_modbusRtuDevice)
        connectModbus();
    else if (m_modbusRtuDevice->state() != QModbusDevice::ConnectedState)
        connectModbus();

    QString sendFrame = QString("%1%2%3").arg(slaveId,2,16,QLatin1Char('0'))
                            .arg(0x06,2,16,QLatin1Char('0'))
                            .arg(startAddress,4,16,QLatin1Char('0'));

    QModbusDataUnit writeUnit = QModbusDataUnit(QModbusDataUnit::HoldingRegisters,startAddress, values.size());
    for(int i=0; i<values.size(); i++){
        writeUnit.setValue(i, values.at(i));
        sendFrame = QString("%1%2").arg(sendFrame).arg(values.at(i),4,16,QLatin1Char('0'));
    }
    QString crc16 = QString("%1").arg(crc16Modbus(QByteArray::fromHex(sendFrame.toUtf8())),4,16,QLatin1Char('0'));
    sendFrame = QString("%1%2").arg(sendFrame,crc16);
    ui->textEdit->append(QString("发送:%1").arg(QString::fromUtf8(QByteArray::fromHex(sendFrame.toUtf8()).toHex(' ').toUpper())));

    QModbusReply *reply = m_modbusRtuDevice->sendWriteRequest(writeUnit,slaveId);
    if (reply) {
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, [this, reply]() {
                if (reply->error() == QModbusDevice::ProtocolError)
                    ui->textEdit->append(QString("Write response error: %1 (Mobus exception: 0x%2)")
                                             .arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16));
                else if (reply->error() != QModbusDevice::NoError)
                    ui->textEdit->append(QString("Write response error: %1 (code: 0x%2)").arg(reply->errorString()).arg(reply->error(), -1, 16));
                else if(reply->error() == QModbusDevice::TimeoutError)
                    ui->textEdit->append(QString("Write response error: %1 (code: 0x%2)")
                                     .arg(reply->errorString()).arg(reply->error(), -1, 16));
                else{
                    const QModbusDataUnit unit = reply->result();
                    m_slaverAddr = reply->serverAddress();
                    QVector<quint16> rValue = unit.values();
                    int start = unit.startAddress();
                    int fcode = 0x00;
                    if(unit.registerType() == QModbusDataUnit::HoldingRegisters)
                        fcode = 0x06;
                    else if(unit.registerType() == QModbusDataUnit::Coils)
                        fcode = 0x05;
                    QString reciveFrame;
                    reciveFrame=QString("%1").arg(m_slaverAddr,2,16,QLatin1Char('0'));
                    reciveFrame=QString("%1%2").arg(reciveFrame).arg(fcode,2,16,QLatin1Char('0'));
                    reciveFrame=QString("%1%2").arg(reciveFrame).arg(start,4,16,QLatin1Char('0'));
                    QString msg;
                    foreach (quint16 value, rValue){
                        reciveFrame=QString("%1%2").arg(reciveFrame).arg(value,4,16,QLatin1Char('0'));
                        msg+=QString("地址%1=%2  ").arg(start++).arg(value);
                    }
                    QString crc16 = QString("%1").arg(crc16Modbus(QByteArray::fromHex(reciveFrame.toUtf8())),4,16,QLatin1Char('0'));
                    reciveFrame +=crc16;
                    ui->textEdit->append(QString("接收:%1").arg(QString::fromUtf8(QByteArray::fromHex(reciveFrame.toUtf8()).toHex(' ').toUpper())));
                    ui->textEdit->append("写值成功,\n"+msg);
                }
                reply->deleteLater();
            });
        }
        else
            reply->deleteLater();
    } else{
        ui->textEdit->append(QString("Write error:") + m_modbusRtuDevice->errorString());
    }

五、扩展功能

​​多线程通信​​:将 Modbus 操作封装到独立线程,避免界面卡顿。
​​动态数据绑定​​:通过信号槽机制实现界面控件与 Modbus 数据的实时同步。
​​协议扩展​​:支持 Modbus TCP 混合模式(需结合 QModbusTcpClient)。

六、完整代码示例

1.mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include 
#include 
#include 

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE



static const quint16 crcTable[] = {
    0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
    0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
    0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
    0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
    0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
    0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
    0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
    0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
    0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
    0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
    0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
    0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
    0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
    0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
    0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
    0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
    0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
    0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
    0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
    0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
    0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
    0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
    0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
    0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
    0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
    0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
    0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
    0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
    0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
    0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
    0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
    0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
};

class MainWindow : public QMainWindow

{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void InitMoubusRtu();
    void connectModbus();
    //写入寄存器 slaveId 写入的从机ID  startAddress写入的起始地址 values写入的值
    void writeUnit(int slaveId, int startAddress, QList<quint16> values);
    //读取寄存器 slaveId 读取的从机ID  startAddress读取的起始地址 readNum读取的数量
    void readUnit(int slaveId, int fcode,int startAddress, int readNum);
    quint16 crc16Modbus(const QByteArray &data);
public slots:
    void onReadModbus();//读取modbus寄存器值
    void disConnectModbus();

private:
    Ui::MainWindow *ui;
    QModbusClient *m_modbusRtuDevice = nullptr;
    QString m_comName;
    int m_slaverAddr;
    int m_parity;
    int m_baud;
    int m_dataBits;
    int m_stopBits;
    QTimer m_timer;
};

#endif // MAINWINDOW_H

2.mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include 

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QStringList listSerial;
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        QSerialPort serial;
        serial.setPort(info);
        listSerial.append(serial.portName());
    }
    ui->numBox->addItems(listSerial);
    connect(ui->openButton,&QPushButton::clicked,this,[=](){
        InitMoubusRtu();
        connectModbus();
    });
    connect(ui->sendButton,&QPushButton::clicked,this,[=](){
        readUnit(ui->slaveAddrEdit->text().toInt(),ui->fcodeComBox->currentText().toInt(),ui->startEdit->text().toInt(),ui->numEdit->text().toInt());
    });
    connect(ui->writeButton,&QPushButton::clicked,this,[=](){
        QList<quint16> values;
        values.push_back(ui->writeEdit->text().toUInt());
        writeUnit(ui->slaveAddrEdit->text().toInt(),ui->startEdit->text().toInt(),values);
    });

    connect(ui->dscBox,&QCheckBox::clicked,this,[=](){
        if(ui->dscBox->checkState()==Qt::Checked){
            m_timer.start(ui->tmEdit->text().toUInt());
            ui->tmEdit->setDisabled(true);
        }
        else{
            m_timer.stop();
            ui->tmEdit->setEnabled(true);
        }
    });
    connect(&m_timer,&QTimer::timeout,this,[=](){
        emit ui->sendButton->clicked();
    });
    connect(ui->clearButton,&QPushButton::clicked,this,[=](){
        ui->textEdit->clear();
    });
}

MainWindow::~MainWindow()
{
    disConnectModbus();
    delete ui;
}

void MainWindow::InitMoubusRtu()
{
    m_comName = ui->numBox->currentText();
    m_baud = ui->btlBox->currentText().toInt();
    m_stopBits = ui->stopBox->currentText().toInt();
    m_dataBits = ui->sjwBox->currentText().toInt();
    switch (ui->jywBox->currentIndex()) {
    case 0:
        m_parity = QSerialPort::NoParity;
        break;
    case 1:
        m_parity = QSerialPort::EvenParity;
        break;
    case 2:
        m_parity = QSerialPort::OddParity;
        break;
    default:
        break;
    }
    m_slaverAddr = ui->slaveAddrEdit->text().toInt();
}

void MainWindow::connectModbus()
{
    disConnectModbus();
    m_modbusRtuDevice = new QModbusRtuSerialMaster(this);
    connect(m_modbusRtuDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        ui->textEdit->append(m_modbusRtuDevice->errorString());
    });
    connect(m_modbusRtuDevice,&QModbusClient::stateChanged,[this](QModbusDevice::State state){
        if(state==QModbusDevice::UnconnectedState){
            ui->textEdit->append("connect ModbusTcp Device failed");
        }
        else if(state==QModbusDevice::ConnectedState){
            ui->textEdit->append("connect ModbusTcp Device success!");
        }
        else if(state==QModbusDevice::ClosingState){
            ui->textEdit->append("connect ModbusTcp Device Closing.");
        }
    });
    m_modbusRtuDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                                              m_comName);
    m_modbusRtuDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                                              m_parity);
    m_modbusRtuDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                                              m_baud);
    m_modbusRtuDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                                              m_dataBits);
    m_modbusRtuDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                                              m_stopBits);
    m_modbusRtuDevice->setTimeout(1000);
    m_modbusRtuDevice->setNumberOfRetries(3);
    m_modbusRtuDevice->connectDevice();
}

void MainWindow::writeUnit(int slaveId, int startAddress, QList<quint16> values)
{
    if (!m_modbusRtuDevice)
        connectModbus();
    else if (m_modbusRtuDevice->state() != QModbusDevice::ConnectedState)
        connectModbus();

    QString sendFrame = QString("%1%2%3").arg(slaveId,2,16,QLatin1Char('0'))
                            .arg(0x06,2,16,QLatin1Char('0'))
                            .arg(startAddress,4,16,QLatin1Char('0'));

    QModbusDataUnit writeUnit = QModbusDataUnit(QModbusDataUnit::HoldingRegisters,startAddress, values.size());
    for(int i=0; i<values.size(); i++){
        writeUnit.setValue(i, values.at(i));
        sendFrame = QString("%1%2").arg(sendFrame).arg(values.at(i),4,16,QLatin1Char('0'));
    }
    QString crc16 = QString("%1").arg(crc16Modbus(QByteArray::fromHex(sendFrame.toUtf8())),4,16,QLatin1Char('0'));
    sendFrame = QString("%1%2").arg(sendFrame,crc16);
    ui->textEdit->append(QString("发送:%1").arg(QString::fromUtf8(QByteArray::fromHex(sendFrame.toUtf8()).toHex(' ').toUpper())));

    QModbusReply *reply = m_modbusRtuDevice->sendWriteRequest(writeUnit,slaveId);
    if (reply) {
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, [this, reply]() {
                if (reply->error() == QModbusDevice::ProtocolError)
                    ui->textEdit->append(QString("Write response error: %1 (Mobus exception: 0x%2)")
                                             .arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16));
                else if (reply->error() != QModbusDevice::NoError)
                    ui->textEdit->append(QString("Write response error: %1 (code: 0x%2)").arg(reply->errorString()).arg(reply->error(), -1, 16));
                else if(reply->error() == QModbusDevice::TimeoutError)
                    ui->textEdit->append(QString("Write response error: %1 (code: 0x%2)")
                                     .arg(reply->errorString()).arg(reply->error(), -1, 16));
                else{
                    const QModbusDataUnit unit = reply->result();
                    m_slaverAddr = reply->serverAddress();
                    QVector<quint16> rValue = unit.values();
                    int start = unit.startAddress();
                    int fcode = 0x00;
                    if(unit.registerType() == QModbusDataUnit::HoldingRegisters)
                        fcode = 0x06;
                    else if(unit.registerType() == QModbusDataUnit::Coils)
                        fcode = 0x05;
                    QString reciveFrame;
                    reciveFrame=QString("%1").arg(m_slaverAddr,2,16,QLatin1Char('0'));
                    reciveFrame=QString("%1%2").arg(reciveFrame).arg(fcode,2,16,QLatin1Char('0'));
                    reciveFrame=QString("%1%2").arg(reciveFrame).arg(start,4,16,QLatin1Char('0'));
                    QString msg;
                    foreach (quint16 value, rValue){
                        reciveFrame=QString("%1%2").arg(reciveFrame).arg(value,4,16,QLatin1Char('0'));
                        msg+=QString("地址%1=%2  ").arg(start++).arg(value);
                    }
                    QString crc16 = QString("%1").arg(crc16Modbus(QByteArray::fromHex(reciveFrame.toUtf8())),4,16,QLatin1Char('0'));
                    reciveFrame +=crc16;
                    ui->textEdit->append(QString("接收:%1").arg(QString::fromUtf8(QByteArray::fromHex(reciveFrame.toUtf8()).toHex(' ').toUpper())));
                    ui->textEdit->append("写值成功,\n"+msg);
                }
                reply->deleteLater();
            });
        }
        else
            reply->deleteLater();
    } else{
        ui->textEdit->append(QString("Write error:") + m_modbusRtuDevice->errorString());
    }
}

void MainWindow::readUnit(int slaveId, int fcode, int startAddress, int readNum)
{
    //数据帧结构:地址码|功能码|起始寄存器地址+寄存器数量
    QString sendFrame = QString("%1%2%3%4").arg(slaveId,2,16,QLatin1Char('0'))
                            .arg(fcode,2,16,QLatin1Char('0'))
                            .arg(startAddress,4,16,QLatin1Char('0'))
                            .arg(readNum,4,16,QLatin1Char('0'));
    QString crc16 = QString("%1").arg(crc16Modbus(QByteArray::fromHex(sendFrame.toUtf8())),4,16,QLatin1Char('0'));
    sendFrame = QString("%1%2").arg(sendFrame,crc16);
    ui->textEdit->append(QString("发送:%1").arg(QString::fromUtf8(QByteArray::fromHex(sendFrame.toUtf8()).toHex(' ').toUpper())));
    QModbusDataUnit read;
    switch (fcode) {
    case 0x01:
        read = QModbusDataUnit(QModbusDataUnit::Coils, startAddress, readNum);//读线圈
        break;
    case 0x02:
        read = QModbusDataUnit(QModbusDataUnit::DiscreteInputs, startAddress, readNum);//读离散量输入
        break;
    case 0x03:
        read = QModbusDataUnit(QModbusDataUnit::HoldingRegisters, startAddress, readNum);//读保持寄存器
        break;
    case 0x04:
        read = QModbusDataUnit(QModbusDataUnit::InputRegisters, startAddress, readNum);//读输入寄存器
        break;
    default:
        ui->textEdit->append("read frame error.");
        return;
        break;
    }
    if (auto *reply = m_modbusRtuDevice->sendReadRequest(read, slaveId)) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &MainWindow::onReadModbus);
        else{
            delete reply;
            connectModbus();
        }
    }
    else{
        ui->textEdit->append("Read error: "+m_modbusRtuDevice->errorString());
        connectModbus();
    }
}

void MainWindow::onReadModbus()
{
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit unit = reply->result();
        m_slaverAddr = reply->serverAddress();
        QVector<quint16> rValue = unit.values();
        int start = unit.startAddress();
        int fcode = 0x00;
        if(unit.registerType() == QModbusDataUnit::HoldingRegisters)
            fcode = 0x03;
        else if(unit.registerType() == QModbusDataUnit::Coils)
            fcode = 0x01;
        else if(unit.registerType() == QModbusDataUnit::DiscreteInputs)
            fcode = 0x02;
        else if(unit.registerType() == QModbusDataUnit::InputRegisters)
            fcode = 0x04;


        QString reciveFrame;
        reciveFrame=QString("%1").arg(m_slaverAddr,2,16,QLatin1Char('0'));
        reciveFrame=QString("%1%2").arg(reciveFrame).arg(fcode,2,16,QLatin1Char('0'));
        reciveFrame=QString("%1%2").arg(reciveFrame).arg(rValue.size()*2,2,16,QLatin1Char('0'));
        QString msg;
        foreach (quint16 value, rValue){
            reciveFrame=QString("%1%2").arg(reciveFrame).arg(value,4,16,QLatin1Char('0'));
            msg+=QString("地址%1=%2  ").arg(start++).arg(value);
        }
        QString crc16 = QString("%1").arg(crc16Modbus(QByteArray::fromHex(reciveFrame.toUtf8())),4,16,QLatin1Char('0'));
        reciveFrame +=crc16;
        ui->textEdit->append(QString("接收:%1").arg(QString::fromUtf8(QByteArray::fromHex(reciveFrame.toUtf8()).toHex(' ').toUpper())));
        ui->textEdit->append(msg);
    }
    else if (reply->error() == QModbusDevice::ProtocolError) {
        ui->textEdit->append(QString("Read response error: %1 (Mobus exception: 0x%2)").
                    arg(reply->errorString()).
                             arg(reply->error(), 2, 16,QLatin1Char('0')));
    }
    else {
        ui->textEdit->append(QString("Read response error: %1 (code: 0x%2)").
                    arg(reply->errorString()).
                             arg(reply->error(), 2, 16,QLatin1Char('0')));
    }
    reply->deleteLater();
}

void MainWindow::disConnectModbus()
{
    if (!m_modbusRtuDevice)
        return;
    m_modbusRtuDevice->disconnectDevice();
    delete m_modbusRtuDevice;
    m_modbusRtuDevice = nullptr;
    ui->textEdit->append("ModbusRtu断开连接");
}

quint16 MainWindow::crc16Modbus(const QByteArray &data)
{
    quint16 crc = 0xFFFF;
    for (auto byte : data)
        crc = (crc >> 8) ^ crcTable[(crc ^ static_cast<quint8>(byte)) & 0xFF];
    return ((crc & 0x00FF) << 8) | ((crc & 0xFF00) >> 8);//高低位互换
}
3.main.cpp
#include "mainwindow.h"
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

七、结语

使用Qt开发Modbus主站软件,需深入理解协议规范与Qt框架的通信机制。通过合理设计异步处理、数据展示及异常恢复策略,可构建高效稳定的工业级工具。开发者可参考开源项目进一步优化功能,适应多样化场景需求。
更多Qt开发实战持续更新中。

你可能感兴趣的:(Qt开发实战教程(含源码),modbus,单片机,物联网,嵌入式硬件,qt,c++,c#)