Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QModbusRtuSerialMaster run in other threads.
Forum Updated to NodeBB v4.3 + New Features

QModbusRtuSerialMaster run in other threads.

Scheduled Pinned Locked Moved Unsolved General and Desktop
10 Posts 6 Posters 1.0k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    sol007
    wrote on 14 Jan 2020, 13:36 last edited by
    #1

    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();
    }
    
    J 1 Reply Last reply 14 Jan 2020, 13:40
    0
    • S sol007
      14 Jan 2020, 13:36

      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();
      }
      
      J Offline
      J Offline
      J.Hilk
      Moderators
      wrote on 14 Jan 2020, 13:40 last edited by J.Hilk
      #2

      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


      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


      Q: What's that?
      A: It's blue light.
      Q: What does it do?
      A: It turns blue.

      S 1 Reply Last reply 14 Jan 2020, 14:21
      3
      • J J.Hilk
        14 Jan 2020, 13:40

        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

        S Offline
        S Offline
        sol007
        wrote on 14 Jan 2020, 14:21 last edited by
        #3

        @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?

        aha_1980A 1 Reply Last reply 14 Jan 2020, 16:02
        0
        • S sol007
          14 Jan 2020, 14:21

          @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?

          aha_1980A Offline
          aha_1980A Offline
          aha_1980
          Lifetime Qt Champion
          wrote on 14 Jan 2020, 16:02 last edited by
          #4

          @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

          Qt has to stay free or it will die.

          S 1 Reply Last reply 15 Jan 2020, 06:10
          1
          • aha_1980A aha_1980
            14 Jan 2020, 16:02

            @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

            S Offline
            S Offline
            sol007
            wrote on 15 Jan 2020, 06:10 last edited by
            #5

            @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.

            J 1 Reply Last reply 15 Jan 2020, 06:24
            1
            • S sol007
              15 Jan 2020, 06:10

              @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.

              J Offline
              J Offline
              J.Hilk
              Moderators
              wrote on 15 Jan 2020, 06:24 last edited by J.Hilk
              #6

              @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


              Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


              Q: What's that?
              A: It's blue light.
              Q: What does it do?
              A: It turns blue.

              S 1 Reply Last reply 16 Jan 2020, 09:06
              2
              • J J.Hilk
                15 Jan 2020, 06:24

                @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

                S Offline
                S Offline
                sol007
                wrote on 16 Jan 2020, 09:06 last edited by sol007
                #7

                @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

                jsulmJ 1 Reply Last reply 16 Jan 2020, 11:54
                0
                • S sol007
                  16 Jan 2020, 09:06

                  @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

                  jsulmJ Offline
                  jsulmJ Offline
                  jsulm
                  Lifetime Qt Champion
                  wrote on 16 Jan 2020, 11:54 last edited by
                  #8

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

                  https://forum.qt.io/topic/113070/qt-code-of-conduct

                  1 Reply Last reply
                  0
                  • H Offline
                    H Offline
                    henry_hust
                    wrote on 8 Jun 2024, 06:39 last edited by
                    #9

                    @sol007 I found the same error, and I solved this error by modify the qserialbus source code!
                    in the file qmodbusrtuserialmaster_p.h

                    class Timer : public QObject
                    {
                        Q_OBJECT
                    
                    public:
                        Timer() = default;
                        Timer(QObject* parent = nullptr):QObject(parent) {} //++++
                       ...
                    };
                    
                    void setupSerialPort()
                        {
                            Q_Q(QModbusRtuSerialMaster);
                            m_responseTimer = new Timer(q);
                            m_serialPort = new QSerialPort(q); //++++
                    ...
                    }
                    
                    Timer *m_responseTimer = nullptr; //Timer m_responseTimer
                        QByteArray m_responseBuffer;
                    
                    

                    The reason is when you move modbus master to another thread using moveToThread, only the modbus master’s children will be move to the new thread, but the object "m_responseTimer" has no parent, it's still in the thread who new the modbus master, that's why the error occured when modbus send timeout. so I modify the source code, also you can solve the error though new modbus master in the new thread.

                    1 Reply Last reply
                    0
                    • 6 Offline
                      6 Offline
                      6_5_4
                      wrote on 29 Mar 2025, 09:15 last edited by
                      #10

                      before moveToThread, QModbusRtuSerialMaster had been created in main thread, and its in a deffrent thread

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved