Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QModbusRtuSerialMaster run in other threads.



  • Hi. I get message "QBasicTimer::start: Timers cannot be started from another thread" when class with member QModbusRtuSerialMaster moved to thread and modbus request was sended. I start internal checkTimer and every ten seconds send modbus requests to my device.
    No message if it isn't moved to another thread. Can anybody help?

    Some fragments of my code

    FuelCounterHandler * fchl=new FuelCounterHandler(fsi);
    fchl->moveToThread(thread_FuelCounters);
        connect(thread_FuelCounters,SIGNAL(started()),fchl,SLOT(start()));
        thread_FuelCounters->start();
    
    class FuelCounterHandler : public QObject
    {
        Q_OBJECT
    public:
        explicit FuelCounterHandler(FuelSystemInfo *fsi, QObject *parent = nullptr);
        ...
    public slots:
        void start();
    private:
        FuelSystemInfo *fsi;
        QTimer *checkTimer;
        QModbusRtuSerialMaster *modbusMasterDevice;
       ...
        void sendRequest(quint8 slaveAdress);
    
    private slots:
        void readyRead();
        ...
    };
    
    void FuelCounterHandler::sendRequest(quint8 slaveAdress){
    
        QModbusReply *reply=nullptr;
        reply=modbusMasterDevice->sendReadRequest(QModbusDataUnit(QModbusDataUnit::HoldingRegisters,0,4),slaveAdress);
        if(reply){
            if (!reply->isFinished()){
            connect(reply, SIGNAL(finished()), this, SLOT(readyRead()));
     
            }
        else
            delete reply; // broadcast replies return immediately
    
    } else {
     // errorhandler()
      
        }
      
    }
    
    void FuelCounterHandler::readyRead(){
    
        auto reply = qobject_cast<QModbusReply *>(sender());
        if (!reply)
            return;
    
        if (reply->error() == QModbusDevice::NoError) {
     // do somthing()
    }
        else{
         // error handler()
        }
        reply->deleteLater();
    }
    

  • Moderators

    HI @sol007 and welcome

    please show us the instantiation of checkTimer and modbusMasterDevice as well as how you do the threading exactly.

    Also information about the used Qt Version is vital!

    That said, why do you Thread it ? You use the non blocking api of QModbusRtuSerial anyway



  • @J-Hilk
    Thank you for answer.
    I use Qt version 5.13.2, minGW 7.3 32bit compiler.

    FuelCounterHandler::FuelCounterHandler(FuelSystemInfo*fsi, QObject *parent) : QObject(parent),fsi(fsi)
    {
        checkTimer=new QTimer(this);
        checkTimer->setSingleShot(false);
        connect(checkTimer,SIGNAL(timeout()),this,SLOT(readCountersValues()));
        modbusMasterDevice= new QModbusRtuSerialMaster(this);
        modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,"COM2");
        modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,QSerialPort::Baud9600);
        modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,QSerialPort::Data8);
        modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::NoParity);
        modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::OneStop);
        modbusMasterDevice->setTimeout(1000);
        modbusMasterDevice->setNumberOfRetries(5);
    }
    

    @J-Hilk said in QModbusRtuSerialMaster run in other threads.:

    That said, why do you Thread it ? You use the non blocking api of QModbusRtuSerial anyway

    Is it mean that QModbuRtuSerial will never block GUI?


  • Lifetime Qt Champion

    @sol007 said in QModbusRtuSerialMaster run in other threads.:

    Is it mean that QModbuRtuSerial will never block GUI?

    Yes. That's what signals&slots are for. Except you use waitFor... function, that is.

    Regards



  • @aha_1980 said in QModbusRtuSerialMaster run in other threads.:

    Except you use waitFor... function, that is.

    I don't use any waitFor...() methods in the thread.


  • Moderators

    @sol007

    I don't use any waitFor...() methods in the thread.

    therefore, unless you do infinite loops while processing the data, QModbusRtuSerialMaster will never block your ui

    I use Qt version 5.13.2, minGW 7.3 32bit compiler.

    That should work fine, older versions, even LTS ones have some modbus trouble 🤷‍♂️

    //block of code

    This actually still does not show us where you do your threading



  • @J-Hilk said in QModbusRtuSerialMaster run in other threads.:

    This actually still does not show us where you do your threading

    I wrote a simple console application that displays the problem.
    file main.cpp:

    #include <QCoreApplication>
    #include <QThread>
    #include "mdb_handler.h"
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        QThread *thread=new QThread();
        MDB_handler *m_handler=new MDB_handler();
        m_handler->moveToThread(thread);
        QObject::connect(thread,SIGNAL(started()),m_handler,SLOT(start()));
        thread->start();
         qDebug()<<"Main threadId="<<QThread::currentThreadId();
        return a.exec();
    }
    

    file mdb_handler.h

    #ifndef MDB_HANDLER_H
    #define MDB_HANDLER_H
    
    #include <QObject>
    #include <QDebug>
    #include <QTimer>
    #include <QModbusDevice>
    #include <QModbusDataUnit>
    #include <QModbusRtuSerialMaster>
    #include <QSerialPort>
    #include <QVariant>
    
    class MDB_handler:public QObject
    {
        Q_OBJECT
    
    private:
        QTimer *checkTimer;
         QModbusRtuSerialMaster *modbusMasterDevice;
    public:
        explicit MDB_handler(QObject * parent=nullptr);
        ~MDB_handler();
    public slots:
        void start();
        void sendrequest();
        void readyRead();
    
    };
    
    #endif // MDB_HANDLER_H
    

    file mdb_hadler.cpp

    #include "mdb_handler.h"
    #include <QThread>
    
    MDB_handler::MDB_handler(QObject *parent) : QObject(parent)
        {
    
            checkTimer =new QTimer(this);
            checkTimer->setSingleShot(false);
            modbusMasterDevice=new QModbusRtuSerialMaster(this);
            modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,QVariant("COM2"));
            modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,QVariant(QSerialPort::Baud9600));
            modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,QVariant(QSerialPort::Data8));
            modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QVariant(QSerialPort::NoParity));
            modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QVariant(QSerialPort::OneStop));
            modbusMasterDevice->setTimeout(1000);
            modbusMasterDevice->setNumberOfRetries(5);
    
            connect(checkTimer,SIGNAL(timeout()),this,SLOT(sendrequest()));
        }
    void MDB_handler::start(){
        qDebug()<<"MDB_handler's threadId="<<QThread::currentThreadId();
        if(modbusMasterDevice->connectDevice()){
         checkTimer->start(10000);
        }
        else {qDebug()<<"Connect failed: "+ modbusMasterDevice->errorString();}
    }
    void MDB_handler::sendrequest(){
            QModbusReply *reply=nullptr;
            reply=modbusMasterDevice->sendReadRequest(QModbusDataUnit(QModbusDataUnit::HoldingRegisters,0,1),1);
            if(reply){
                if (!reply->isFinished())
                connect(reply, SIGNAL(finished()), this, SLOT(readyRead()));
            else
                delete reply; // broadcast replies return immediately
    
        } else {
                qDebug()<<modbusMasterDevice->errorString();
            }
        }
    
    void MDB_handler::readyRead(){
        auto reply = qobject_cast<QModbusReply *>(sender());
        if (!reply)
            return;
    
        if (reply->error() == QModbusDevice::NoError) {
            const QModbusDataUnit unit = reply->result();
            qDebug()<<"Value of register is "<<unit.value(0);
    
        }
        else{ qDebug()<<(QString("Read response error: %1 (code: 0x%2)").
                                   arg(reply->errorString()).
                                   arg(reply->error(), -1, 16));}
        reply->deleteLater();
    }
    MDB_handler::~MDB_handler(){
        checkTimer->stop();
        modbusMasterDevice->disconnectDevice();
        delete modbusMasterDevice;
        delete checkTimer;
    }
    

    Output console result is:

    "Main thread_ID= 0x600
    MDB_handler's thread_ID= 0x504
    QBasicTimer::start: Timers cannot be started from another thread
    Value of register is  1402"
    

    If MDB_handler run in another thread I will never get timeout error;
    If I run MDB_handler in main thread I don't get message about QBasicTimer!

    I think that QBasicTimer starts for timeout.

    Now I do not need to send requests in a separate thread.
    In future I will develop a system which needs sending requests in a loop.
    I think it makes sense to move the QModbusRtuSerialMaster to a separate thread


  • Qt Champions 2019

    @sol007 Still not sure why you think you need a thread


Log in to reply