Modbus problem, beginner



  • Hello,

    I new to qt, and to programming at all.
    I learned a bit of Basic (PICBasic), and i quite understand the way it works.
    Now i want to learn c++ to make a modbus programm.

    I already understand a bit of it, but now i encountered a problem.

    When i connect my PC to the modbus device, and press connect, it says no device connected. If i use an other programm it workes.

    I made a modification on the modbus master example, and thought that was the problem, but when i open master, and try to connect that, i get the same.

    Can it be something with me using W8.1 and the MingW32 compiler?

    I can not post the code now, because it is on my other pc, but the connection part is the same as on master. And this one is also not working.
    I tested the connection with MBreader, and this worked.

    Can it also be that QT askes a connection confirmation? And MBreader just starts to poll?

    Serial, 9600, no par. 1 stop, 8 data.
    This is also set.

    Thanks


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    You should provide more information:

    • Version of Qt
    • What type of hardware are you connecting to ?
    • What kind of device are you trying to connect to ?


  • Thanks,
    On the laptop that i am using for the connection i have :

    Qt Creator 4.4.1
    Based on Qt 5.9.2 (MSVC 2015, 32 bit)
    

    The hardware is an alarm/monitoring system.
    It is made to monitor some data, like temperatures, and if they exceed a certain point, it will give alarm. The whole system is controlled/checked by a display.
    On the system is a modbus connection, so you can add a computer to it, to create your own visualisation of the data.

    This all works, if i connect with MBreader. Today i did a check with CASmodbus scanner, and i saw this one also askes for a connection, and got it. But with my QT programm, and with (examples) master it does not connect.

    I will add the code. It is made as connection test, so i do have some remarks and old stuff still in there.

    Project:

    #-------------------------------------------------
    #
    # Project created by QtCreator 2017-10-17T19:11:13
    #
    #-------------------------------------------------
    
    QT       += core gui serialport serialbus
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    TARGET = Noris_test
    TEMPLATE = app
    
    # The following define makes your compiler emit warnings if you use
    # any feature of Qt which has been marked as deprecated (the exact warnings
    # depend on your compiler). Please consult the documentation of the
    # deprecated API in order to know how to port your code away from it.
    DEFINES += QT_DEPRECATED_WARNINGS
    
    # You can also make your code fail to compile if you use deprecated APIs.
    # In order to do so, uncomment the following line.
    # You can also select to disable deprecated APIs only up to a certain version of Qt.
    #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
    
    
    SOURCES += \
            main.cpp \
            mainwindow.cpp \
        settings.cpp
    
    HEADERS += \
            mainwindow.h \
        settings.h
    
    FORMS += \
            mainwindow.ui \
        settings.ui
    
    

    Header mainwindow:

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QtSerialPort/QSerialPort>
    #include <QtSerialBus/QModbusRtuSerialMaster>
    #include <QtSerialBus/QtSerialBus>
    #include <QModbusDataUnit>
    #include "settings.h"
    
    
    QT_BEGIN_NAMESPACE
    
    class QModbusClient;
    class QModbusReply;
    
    namespace Ui {
    class MainWindow;
    class Settings;
    }
    
    QT_END_NAMESPACE
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    
    private:
        QModbusDataUnit readRequestIR() const;
        QModbusDataUnit readRequestCO() const;
        QModbusDataUnit readRequestHR() const;
        QModbusDataUnit readRequestDI() const;
        Settings *settingswindow;
    
    private slots:
    
    //    void serialReceived();
        void readReadyCO();
        void readReadyDI();
        void readReadyIR();
        void readReadyHR();
        void onStateChanged(int state);
    
    
    
        void on_PB_dis_clicked();
    
        void on_PB_con_clicked();
    
        void on_actionExit_triggered();
    
    private slots:
    
        void updateRPM(const QString);
    
        void on_actionSettings_triggered();
    
    private:
        Ui::MainWindow *ui;
        QModbusReply *lastRequest;
        QModbusClient *modbusDevice;
        Settings *m_settings;
    
    
    };
    
    #endif // MAINWINDOW_H
    
    

    Header Settings

    #ifndef SETTINGS_H
    #define SETTINGS_H
    
    #include <QMainWindow>
    #include <QDialog>
    #include <QSerialPort>
    
    namespace Ui {
    class Settings;
    }
    
    class Settings : public QMainWindow
    {
        Q_OBJECT
    
    public:
        struct Startsettings {
            QString port ;
            int parity = QSerialPort::NoParity;
            int baud = QSerialPort::Baud9600;
            int dataBits = QSerialPort::Data8;
            int stopBits = QSerialPort::OneStop;
            int responseTime = 10000;
            int numberOfRetries = 3;
    
        };
    
        explicit Settings(QWidget *parent = 0);
        ~Settings();
    
        Startsettings startsettings() const;
    
    private slots:
        void on_closeButton_clicked();
    
    private:
        Startsettings m_settings;
        Ui::Settings *ui;
    };
    
    #endif // SETTINGS_H
    
    

    Source mainwindow:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QtSerialPort/QSerialPort>
    #include <QtSerialPort/QSerialPortInfo>
    #include <QtSerialBus>
    #include <QModbusRtuSerialMaster>
    #include <QStandardItemModel>
    #include <QStatusBar>
    #include <string>
    #include <QDebug>
    #include <QMessageBox>
    #include "qmessagebox.h"
    #include "settings.h"
    
    enum ModbusConnection {
       Serial
    };
    
    
    //QSerialPort *serial;
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
        , lastRequest(nullptr)
        , modbusDevice(nullptr)
    {
        ui->setupUi(this);
        m_settings = new Settings(this);
        statusBar()->showMessage("Check Settings before connecting");
    
    
    
    
    //    serial = new QSerialPort(this);
    //    serial->setPortName("com1");
    //    serial->setBaudRate(QSerialPort::Baud9600);
    //    serial->setDataBits(QSerialPort::Data8);
    //    serial->setParity(QSerialPort::NoParity);
    //    serial->setStopBits(QSerialPort::OneStop);
    //    serial->setFlowControl(QSerialPort::NoFlowControl);
    //    serial->open(QIODevice::ReadWrite);
    //    connect(serial, SIGNAL(readyRead()),this,SLOT(serialReceived()));
    
    
    
    
        ui->RPM->display("-----");
        ui->HTT->display("-----");
        ui->HTP->display("-----");
    
    //    const auto ports = QSerialPortInfo::availablePorts();
    //    for (const QSerialPortInfo &info : ports)
    //        ui->serialPortCombo->addItem(info.portName(), false);
    
    
    
    
    
    
    
    
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
        if (modbusDevice)
            modbusDevice->disconnectDevice();
        delete modbusDevice;
    }
    
    
    //void MainWindow::serialReceived()
    
    //{
    //    QByteArray ba;
    //    ba=serial->readAll();
    //    ui->label_4->setText(ba);
    //    qDebug()<<ba;
    
    //}
    
    void MainWindow::on_PB_dis_clicked()
    {
        if (modbusDevice)
            modbusDevice->disconnectDevice();
        delete modbusDevice;
        ui->statusBar->showMessage("Disconnecting",5000);
    }
    
    void MainWindow::on_PB_con_clicked()
    {
    
    
        QMessageBox msgBox;
        msgBox.setWindowTitle("CONNECTING");
        msgBox.setText("Please check your settings before connecting");
    
        QAbstractButton*
                pbuttonyes =
                msgBox.addButton("Settings", QMessageBox::YesRole);
                msgBox.addButton("No", QMessageBox::NoRole);
                msgBox.exec();
    if (msgBox.clickedButton()==pbuttonyes){
    
           settingswindow =new Settings(this);
           settingswindow->setFixedSize(250, 360);
           settingswindow->setWindowTitle("Settings");
           settingswindow->show();
       return;
       }
       else {
    
    
    
    
    
        if (!modbusDevice)
            ui->statusBar->showMessage("No device connected");
            return;
    
        statusBar()->clearMessage();
        if (modbusDevice->state() != QModbusDevice::ConnectedState) {
    
                    modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                    m_settings->startsettings().port);
                    modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                        m_settings->startsettings().parity);
                    modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                        m_settings->startsettings().baud);
                    modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                        m_settings->startsettings().dataBits);
                    modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                        m_settings->startsettings().stopBits);
    
    }
        modbusDevice->setTimeout(m_settings->startsettings().responseTime);
        modbusDevice->setNumberOfRetries(m_settings->startsettings().numberOfRetries);;
                if (!modbusDevice->connectDevice()) {
                    statusBar()->showMessage(tr("Connect failed: ") + modbusDevice->errorString(), 5000);
                }else{ statusBar()->showMessage("Connected");
    }
    
    
                if (!modbusDevice)
                    return;
                statusBar()->clearMessage();
    
    
    //begin lezen CO1
    if (auto *replyCO = modbusDevice->sendReadRequest(readRequestCO(), 1)) {
    if (!replyCO->isFinished())
    connect(replyCO, &QModbusReply::finished, this, &MainWindow::readReadyCO);
    else
    delete replyCO; // broadcast replies return immediately
    } else {
    statusBar()->showMessage(tr("Read CO error: ") + modbusDevice->errorString(), 5000);
    }
    
    //einde lezen CO1
    
    //begin lezen DI1
    if (auto *replyDI = modbusDevice->sendReadRequest(readRequestDI(), 1)) {
    if (!replyDI->isFinished())
    connect(replyDI, &QModbusReply::finished, this, &MainWindow::readReadyDI);
    else
    delete replyDI; // broadcast replies return immediately
    } else {
    statusBar()->showMessage(tr("Read DI error: ") + modbusDevice->errorString(), 5000);
    }
    
    //einde lezen DI1
    
    //begin lezen IR1
    if (auto *replyIR = modbusDevice->sendReadRequest(readRequestIR(), 1)) {
    if (!replyIR->isFinished())
    connect(replyIR, &QModbusReply::finished, this, &MainWindow::readReadyIR);
    else
    delete replyIR; // broadcast replies return immediately
    } else {
    statusBar()->showMessage(tr("Read IR error: ") + modbusDevice->errorString(), 5000);
    }
    
    //einde lezen IR1
    
    //begin lezen HR1
    if (auto *replyHR = modbusDevice->sendReadRequest(readRequestHR(), 1)) {
    if (!replyHR->isFinished())
    connect(replyHR, &QModbusReply::finished, this, &MainWindow::readReadyHR);
    else
    delete replyHR; // broadcast replies return immediately
    } else {
    statusBar()->showMessage(tr("Read HR error: ") + modbusDevice->errorString(), 5000);
    }
    
    //einde lezen HR1
    
    
    
    
       }}
    
    
    
    
    //readrequest 1
    QModbusDataUnit MainWindow::readRequestCO() const
    {
       const auto table =
            static_cast<QModbusDataUnit::RegisterType> (QModbusDataUnit::Coils);
    
        int startAddress1 = (100);
        int numberOfEntries1 = (5);
       return QModbusDataUnit(table, startAddress1, numberOfEntries1);
    }
    
    QModbusDataUnit MainWindow::readRequestDI() const
    {
       const auto table =
            static_cast<QModbusDataUnit::RegisterType> (QModbusDataUnit::DiscreteInputs);
    
        int startAddress1 = (100);
        int numberOfEntries1 = (5);
       return QModbusDataUnit(table, startAddress1, numberOfEntries1);
    }
    
    QModbusDataUnit MainWindow::readRequestIR() const
    {
       const auto table =
            static_cast<QModbusDataUnit::RegisterType> (QModbusDataUnit::InputRegisters);
    
        int startAddress1 = (100);
        int numberOfEntries1 = (5);
       return QModbusDataUnit(table, startAddress1, numberOfEntries1);
    }
    
    QModbusDataUnit MainWindow::readRequestHR() const
    {
       const auto table =
            static_cast<QModbusDataUnit::RegisterType> (QModbusDataUnit::HoldingRegisters);
    
        int startAddress1 = (100);
        int numberOfEntries1 = (5);
       return QModbusDataUnit(table, startAddress1, numberOfEntries1);
    }
    
    
    
    
    //readrequest einde
    
    void MainWindow::readReadyCO()
    {
    {
    auto replyCO = qobject_cast<QModbusReply *>(sender());
    if (!replyCO)
    return;
    
    if (replyCO->error() == QModbusDevice::NoError) {
    const QModbusDataUnit unit = replyCO->result();
    for (uint i = 0; i < unit.valueCount(); i++) {
        const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
                                 .arg(QString::number(unit.value(i),
                                      unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
    //    ui->label_4->setText(entry);
    }
    } else if (replyCO->error() == QModbusDevice::ProtocolError) {
    statusBar()->showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)").
                                arg(replyCO->errorString()).
                                arg(replyCO->rawResult().exceptionCode(), -1, 16), 5000);
    } else {
    statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").
                                arg(replyCO->errorString()).
                                arg(replyCO->error(), -1, 16), 5000);
    }
    
    replyCO->deleteLater();
    }
    
    }
    void MainWindow::readReadyDI()
    {
    {
    auto replyDI = qobject_cast<QModbusReply *>(sender());
    if (!replyDI)
    return;
    
    if (replyDI->error() == QModbusDevice::NoError) {
    const QModbusDataUnit unit = replyDI->result();
    for (uint i = 0; i < unit.valueCount(); i++) {
        const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
                                 .arg(QString::number(unit.value(i),
                                      unit.registerType() <= QModbusDataUnit::DiscreteInputs ? 10 : 16));
    //    ui->label_4->setText(entry);
    }
    } else if (replyDI->error() == QModbusDevice::ProtocolError) {
    statusBar()->showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)").
                                arg(replyDI->errorString()).
                                arg(replyDI->rawResult().exceptionCode(), -1, 16), 5000);
    } else {
    statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").
                                arg(replyDI->errorString()).
                                arg(replyDI->error(), -1, 16), 5000);
    }
    
    replyDI->deleteLater();
    }
    
    }
    void MainWindow::readReadyIR()
                {
        {
            auto replyIR = qobject_cast<QModbusReply *>(sender());
            if (!replyIR)
                return;
    
            if (replyIR->error() == QModbusDevice::NoError) {
                const QModbusDataUnit unit = replyIR->result();
                for (uint i = 0; i < unit.valueCount(); i++) {
                    const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
                                             .arg(QString::number(unit.value(i),
                                                  unit.registerType() <= QModbusDataUnit::InputRegisters ? 10 : 16));
                    MainWindow::updateRPM(entry[8]);
                    ui->label_4->setText(entry);
    
                }
            } else if (replyIR->error() == QModbusDevice::ProtocolError) {
                statusBar()->showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)").
                                            arg(replyIR->errorString()).
                                            arg(replyIR->rawResult().exceptionCode(), -1, 16), 5000);
            } else {
                statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").
                                            arg(replyIR->errorString()).
                                            arg(replyIR->error(), -1, 16), 5000);
            }
    
            replyIR->deleteLater();
        }
    
    }
    
    void MainWindow::updateRPM(const QString RPMreading)
    
    {
    ui->RPM->display(RPMreading);
    }
    
    void MainWindow::readReadyHR()
    {
    {
    auto replyHR = qobject_cast<QModbusReply *>(sender());
    if (!replyHR)
    return;
    
    if (replyHR->error() == QModbusDevice::NoError) {
    const QModbusDataUnit unit = replyHR->result();
    for (uint i = 0; i < unit.valueCount(); i++) {
        const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
                                 .arg(QString::number(unit.value(i),
                                      unit.registerType() <= QModbusDataUnit::HoldingRegisters ? 10 : 16));
    //    ui->label_4->setText(entry);
    }
    } else if (replyHR->error() == QModbusDevice::ProtocolError) {
    statusBar()->showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)").
                                arg(replyHR->errorString()).
                                arg(replyHR->rawResult().exceptionCode(), -1, 16), 5000);
    } else {
    statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").
                                arg(replyHR->errorString()).
                                arg(replyHR->error(), -1, 16), 5000);
    }
    
    replyHR->deleteLater();
    }
    
    }
    
    void MainWindow::onStateChanged(int state)
    {
    
        if (state == QModbusDevice::UnconnectedState)
            statusBar()->showMessage("Disconnected");
        else if (state == QModbusDevice::ConnectedState)
            statusBar()->showMessage("Connected");
    }
    
    void MainWindow::on_actionExit_triggered()
    {
        MainWindow::close();
    }
    
    
    
    void MainWindow::on_actionSettings_triggered()
    {
        settingswindow =new Settings(this);
        settingswindow->setFixedSize(250, 360);
        settingswindow->setWindowTitle("Settings");
        settingswindow->show();
    }
    
    

    Source main:

    #include "mainwindow.h"
    
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.setFixedSize(280, 500);
        w.setWindowTitle("Noris Test");
        w.show();
    
    
        return a.exec();
    }
    
    

    source settings:

    #include "settings.h"
    #include "ui_settings.h"
    #include <QSerialPortInfo>
    #include <QtSerialBus>
    #include "mainwindow.h"
    
    Settings::Settings(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::Settings)
    {
        ui->setupUi(this);
        const auto ports = QSerialPortInfo::availablePorts();
        for (const QSerialPortInfo &info : ports)
    
        ui->comCombo2->addItem(info.portName(), false);
        ui->comCombo2->setCurrentIndex(1);
        ui->parityCombo->setCurrentIndex(0);
        ui->baudCombo->setCurrentText(QString::number(m_settings.baud));
        ui->dataBitsCombo->setCurrentText(QString::number(m_settings.dataBits));
        ui->stopBitsCombo->setCurrentText(QString::number(m_settings.stopBits));
        ui->timeoutSpinner->setValue(m_settings.responseTime);
        ui->retriesSpinner->setValue(m_settings.numberOfRetries);
    
    
    
    
    
    
    
    
    
        connect(ui->applyButton, &QPushButton::clicked, [this]() {
            m_settings.port = ui->comCombo2->currentText();
            m_settings.parity = ui->parityCombo->currentIndex();
            if (m_settings.parity > 0)
                m_settings.parity++;
            m_settings.baud = ui->baudCombo->currentText().toInt();
            m_settings.dataBits = ui->dataBitsCombo->currentText().toInt();
            m_settings.stopBits = ui->stopBitsCombo->currentText().toInt();
            m_settings.responseTime = ui->timeoutSpinner->value();
            m_settings.numberOfRetries = ui->retriesSpinner->value();
    
            ui->statusbar->showMessage("Settings saved, press close.");
        });
    }
    
    Settings::~Settings()
    {
        delete ui;
    }
    
    
    Settings::Startsettings Settings::startsettings() const
    {
        return m_settings;
    }
    
    void Settings::on_closeButton_clicked()
    {
        hide();
    }
    
    

  • Lifetime Qt Champion

    Can you give the name of the device ?



  • I only have the maker of the device, Thats Noris Automation, this is all I have, the system was build and running before I got to know it. And this is written on the display.


  • Lifetime Qt Champion

    You didn't get any documentation for that device ?



  • No, not that i am aware of. But the strange thing in my opinion is that the other programms do work.


  • Lifetime Qt Champion

    It is indeed. What exact error message are you getting ?


  • Moderators

    @GhostWolf just a question on my side, did you check the Modbus-Settings of the ModbusMaster example? For whatever reason, its default Baud Rate is, IIRC 19200, and not the 9600 your device is supposed to have.



  • Thanks for the answers, I found the connection problem. I added the part:

    void MainWindow::on_connectType_currentIndexChanged(int index)
    {
    if (modbusDevice) {
    modbusDevice->disconnectDevice();
    delete modbusDevice;
    modbusDevice = nullptr;
    }

    auto type = static_cast<ModbusConnection> (index);
    if (type == Serial) {
        modbusDevice = new QModbusRtuSerialMaster(this);
    } else if (type == Tcp) {
        modbusDevice = new QModbusTcpClient(this);
        if (ui->portEdit->text().isEmpty())
            ui->portEdit->setText(QLatin1Literal("127.0.0.1:502"));
    }
    
    connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        statusBar()->showMessage(modbusDevice->errorString(), 5000);
    });
    
    if (!modbusDevice) {
        ui->connectButton->setDisabled(true);
        if (type == Serial)
            statusBar()->showMessage(tr("Could not create Modbus master."), 5000);
        else
            statusBar()->showMessage(tr("Could not create Modbus client."), 5000);
    } else {
        connect(modbusDevice, &QModbusClient::stateChanged,
                this, &MainWindow::onStateChanged);
    }
    

    }

    And now I can make a connection.

    Now I have to test it on the device, but i can't access it for a while, to be continued