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. QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.
Forum Updated to NodeBB v4.3 + New Features

QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.

Scheduled Pinned Locked Moved Unsolved General and Desktop
15 Posts 5 Posters 1.1k Views 1 Watching
  • 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.
  • R Offline
    R Offline
    Rufledore
    wrote on last edited by
    #1

    Hello,
    I try to make a shared library for communication with a device via TCP or Serial communication. So I have created Channel class with QIODevice* member and two derived classes that instantiate this QIODevice* to SerialPort* or to TCPSocket* and use it for communication.

    The problem is that when I test the library in console application without QApplication::exec() it works very well with TCPSocket, but doesn't work with SerialPort. I found out that the QSerialPort::readyRead() is not emitted.

    I asked for help during the Qt Summit 2020 and they told me that I need to run an EventLoop. The thing is that the QEventLoop can't be ran without QApplication, but the QApplication can be ran only on the main thread and the exec() method blocks it. I tested the library with Qt UI project and it worked fine. However I want my library to be usable for all type of users, not only Qt ones. So now I am looking for a decision. If someone knows how to fix this, please tell me?

    The strangest thing for me is that QTcpSocket and all other connections between signals and slots that I have created work just fine, only QSerialPort::readyRead() doesn't. Why? Why only the SerialPort::readyRead() can't work without QApplication::exec()?

    1 Reply Last reply
    0
    • Christian EhrlicherC Online
      Christian EhrlicherC Online
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Please provide a minimal, working example. You need to call the initial Qt event loop with QCoreApplication::exec() then all works fine. Please also take a look at the various examples.

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      1 Reply Last reply
      2
      • R Offline
        R Offline
        Rufledore
        wrote on last edited by Rufledore
        #3

        @Christian-Ehrlicher said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

        Please provide a minimal, working example. You need to call the initial Qt event loop with QCoreApplication::exec() then all works fine. Please also take a look at the various examples.

        Hello and thanks for your answer!
        Firstly for calling Qt event loop with QCoreApplication::exec() - I have tried it but it also blocks the thread and the client application. I have tried to move it to background thread, but then I got another warnings and errors (for example "QCoreApplication::exec: Must be called from the main thread"). I also get error: "QEventDispatcherWin32::registerTimer: timers cannot be started from another thread".

        Secondly, as you asked, a simplified code example. The implementations are included only for the necessary functions.

        
        class MLXCOMChannel : public QObject
        {
            Q_OBJECT
        
        protected:
            QSharedPointer<QIODevice> ch_communicationDevice;
        
            ErrorHandler ch_errorHandler;
            QMutex ch_mutex;
            bool ch_open;
            uint64_t ch_sentDataSize;
            QByteArray ch_dataBuffer;
        
        private:
        
        
        private slots:
            void                    handleMLXCOMError(MLXCOMErrorType errorType);
        
        protected slots:
            void                    readData() {
                                            if (ch_communicationDevice.data()) {
                                        
                                                QMutexLocker locker(&ch_mutex);
                                        
                                                ch_dataBuffer.append(ch_communicationDevice->readAll());
                                        
                                            } else {
                                        
                                                emit error(MLXCOMErrorType::FunctionalityNotImplemented);
                                        
                                            }                        };
            void                    dataWritten(qint64 bytes);
        
        public:
        
                                    MLXCOMChannel();
            virtual                 ~MLXCOMChannel();
        
            virtual void            open();
            virtual void            close();
            virtual void            sendData(const QByteArray &data);
            virtual QByteArray      receiveData(size_t dataSize);
            virtual bool            hasData();
            virtual qint32          availableDataSize();
        
        
            // Channels settings interaction
            t_ChannelType getChannelType();
            std::string getChannelTypeName();
        
            static qint32           createChannel(MLXCOMChannel **channel, const t_ChannelType channelType);
            static qint32           destroyChannel(MLXCOMChannel **channel);
        
        signals:
            void                    error(MLXCOMChannel::MLXCOMErrorType errorType) const;
        
        };
        
        
        // The one that works - TCP Channel
        class TCPChannel : public MLXCOMChannel
        {
            Q_OBJECT
        
        public:
            TCPChannel() {
                    ch_communicationDevice.reset(new QTcpSocket);
                
                    m_pSocket = reinterpret_cast<QTcpSocket*>(ch_communicationDevice.get());
                
                
                
                    connect(m_pSocket, &QTcpSocket::disconnected, this, [=](){ch_open = false;});
                
                    connect(m_pSocket, &QTcpSocket::connected, this, [=](){ch_open = true;});        
                
                    connect(m_pSocket, &QIODevice::readyRead, this, &TCPChannel::readData);
                
                    connect(m_pSocket, &QIODevice::bytesWritten, this, &TCPChannel::dataWritten);
                
                    connect(m_pSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &TCPChannel::handleSocketError);   ~TCPChannel() override;
        
            void open() override;
            void close() override;
            void sendData(const QByteArray &data) override;
        
        private:
        
            QTcpSocket *m_pSocket;
        
        
        private slots:
            void handleSocketError();
        };
        
        
        // The one that does not work - Serial Channel
        class SerialChannel : public MLXCOMChannel
        {
            Q_OBJECT
        
        public:
            SerialChannel() {
                ch_communicationDevice.reset(new QSerialPort);
            
                m_pSerialPort = reinterpret_cast<QSerialPort*>(ch_communicationDevice.get());
                
                connect(this, &SerialChannel::debugSignal, this, &SerialChannel::debugSlot);
            
                connect(m_pSerialPort, &QSerialPort::readyRead, this, &SerialChannel::readData);
            
                connect(m_pSerialPort, &QIODevice::bytesWritten, this, &SerialChannel::dataWritten);
            
                connect(m_pSerialPort, &QSerialPort::errorOccurred, this, &SerialChannel::handlePortError);;
            ~SerialChannel() override;
        
            // Overridden methods
            void open() override;
            void close() override;                              // To be moved to MLXCOMChannel
            void sendData(const QByteArray &data) override;     // To be moved to MLXCOMChannel
        
        
        private:
            QSerialPort *m_pSerialPort;
        
        
        private slots:
            void handlePortError();
            void debugSlot(QString &msg);
        signals:
            void debugSignal(QString &msg);
        
        };
        

        So, the TCPChannel works just fine. The debugSignal and debugSlot in SerialChannel work fine. Error handling signals work fine. Only the QSerialPort::readyRead doesn't.

        jsulmJ 1 Reply Last reply
        0
        • R Rufledore

          @Christian-Ehrlicher said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

          Please provide a minimal, working example. You need to call the initial Qt event loop with QCoreApplication::exec() then all works fine. Please also take a look at the various examples.

          Hello and thanks for your answer!
          Firstly for calling Qt event loop with QCoreApplication::exec() - I have tried it but it also blocks the thread and the client application. I have tried to move it to background thread, but then I got another warnings and errors (for example "QCoreApplication::exec: Must be called from the main thread"). I also get error: "QEventDispatcherWin32::registerTimer: timers cannot be started from another thread".

          Secondly, as you asked, a simplified code example. The implementations are included only for the necessary functions.

          
          class MLXCOMChannel : public QObject
          {
              Q_OBJECT
          
          protected:
              QSharedPointer<QIODevice> ch_communicationDevice;
          
              ErrorHandler ch_errorHandler;
              QMutex ch_mutex;
              bool ch_open;
              uint64_t ch_sentDataSize;
              QByteArray ch_dataBuffer;
          
          private:
          
          
          private slots:
              void                    handleMLXCOMError(MLXCOMErrorType errorType);
          
          protected slots:
              void                    readData() {
                                              if (ch_communicationDevice.data()) {
                                          
                                                  QMutexLocker locker(&ch_mutex);
                                          
                                                  ch_dataBuffer.append(ch_communicationDevice->readAll());
                                          
                                              } else {
                                          
                                                  emit error(MLXCOMErrorType::FunctionalityNotImplemented);
                                          
                                              }                        };
              void                    dataWritten(qint64 bytes);
          
          public:
          
                                      MLXCOMChannel();
              virtual                 ~MLXCOMChannel();
          
              virtual void            open();
              virtual void            close();
              virtual void            sendData(const QByteArray &data);
              virtual QByteArray      receiveData(size_t dataSize);
              virtual bool            hasData();
              virtual qint32          availableDataSize();
          
          
              // Channels settings interaction
              t_ChannelType getChannelType();
              std::string getChannelTypeName();
          
              static qint32           createChannel(MLXCOMChannel **channel, const t_ChannelType channelType);
              static qint32           destroyChannel(MLXCOMChannel **channel);
          
          signals:
              void                    error(MLXCOMChannel::MLXCOMErrorType errorType) const;
          
          };
          
          
          // The one that works - TCP Channel
          class TCPChannel : public MLXCOMChannel
          {
              Q_OBJECT
          
          public:
              TCPChannel() {
                      ch_communicationDevice.reset(new QTcpSocket);
                  
                      m_pSocket = reinterpret_cast<QTcpSocket*>(ch_communicationDevice.get());
                  
                  
                  
                      connect(m_pSocket, &QTcpSocket::disconnected, this, [=](){ch_open = false;});
                  
                      connect(m_pSocket, &QTcpSocket::connected, this, [=](){ch_open = true;});        
                  
                      connect(m_pSocket, &QIODevice::readyRead, this, &TCPChannel::readData);
                  
                      connect(m_pSocket, &QIODevice::bytesWritten, this, &TCPChannel::dataWritten);
                  
                      connect(m_pSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &TCPChannel::handleSocketError);   ~TCPChannel() override;
          
              void open() override;
              void close() override;
              void sendData(const QByteArray &data) override;
          
          private:
          
              QTcpSocket *m_pSocket;
          
          
          private slots:
              void handleSocketError();
          };
          
          
          // The one that does not work - Serial Channel
          class SerialChannel : public MLXCOMChannel
          {
              Q_OBJECT
          
          public:
              SerialChannel() {
                  ch_communicationDevice.reset(new QSerialPort);
              
                  m_pSerialPort = reinterpret_cast<QSerialPort*>(ch_communicationDevice.get());
                  
                  connect(this, &SerialChannel::debugSignal, this, &SerialChannel::debugSlot);
              
                  connect(m_pSerialPort, &QSerialPort::readyRead, this, &SerialChannel::readData);
              
                  connect(m_pSerialPort, &QIODevice::bytesWritten, this, &SerialChannel::dataWritten);
              
                  connect(m_pSerialPort, &QSerialPort::errorOccurred, this, &SerialChannel::handlePortError);;
              ~SerialChannel() override;
          
              // Overridden methods
              void open() override;
              void close() override;                              // To be moved to MLXCOMChannel
              void sendData(const QByteArray &data) override;     // To be moved to MLXCOMChannel
          
          
          private:
              QSerialPort *m_pSerialPort;
          
          
          private slots:
              void handlePortError();
              void debugSlot(QString &msg);
          signals:
              void debugSignal(QString &msg);
          
          };
          

          So, the TCPChannel works just fine. The debugSignal and debugSlot in SerialChannel work fine. Error handling signals work fine. Only the QSerialPort::readyRead doesn't.

          jsulmJ Offline
          jsulmJ Offline
          jsulm
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @Rufledore said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

          I have tried it but it also blocks the thread and the client application

          Please show how you did it. I hope you did it in your main.cpp

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

          1 Reply Last reply
          1
          • R Offline
            R Offline
            Rufledore
            wrote on last edited by Rufledore
            #5

            @jsulm said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

            As I mentioned in the first post it is a part of a shared library that I develop. So, this library has export functions like these:

            MLXCOM_API int MLXCOM_CALL_CONV mlxcom_send_data(MLXCOM_CHANNEL mlxcomChannel, char* data, uint32_t dataSize) {
                MLXCOMChannel* channel = static_cast<MLXCOMChannel*>(mlxcomChannel);
                QByteArray dataForSend(data, dataSize);
            
                channel->prepareErrorHandler();
                channel->sendData(dataForSend);
                if (channel->hasNewError()) {
                    return channel->lastError().first;
                }
            
                return 0;
            }
            
            // add parameter - number of bytes.
            MLXCOM_API int MLXCOM_CALL_CONV mlxcom_receive_data(MLXCOM_CHANNEL mlxcomChannel, char* data, uint32_t data_size) {
                MLXCOMChannel* channel = static_cast<MLXCOMChannel*>(mlxcomChannel);
            
                channel->prepareErrorHandler();
                QByteArray msg = channel->receiveData(data_size);
                if (msg.size() > 0) {
                    std::memcpy(data, msg.data(), static_cast<size_t>(msg.size() + 1));
                }
                if (channel->hasNewError()) {
                    return channel->lastError().first;
                }
            
            
                return 0;
            }
            
            

            So I added a function init() where I did QCoreApplication::exec().

            I use the library in a test console application that is only a main(), but doesn't have QApplication or QCoreApplication in it. There I faced the issue. When I use the library in a test UI app that is simply a MainWindow started from main() and QApplication.exec() it works okay. However I am willing do make cross platform C-style library that could be used not only in Qt. TCP communication class works well also when I use it in visual studio.

            1 Reply Last reply
            0
            • R Offline
              R Offline
              Rufledore
              wrote on last edited by
              #6

              Okay, now I tried it and the static way QCoreApplication::exec() doesn't block but throw : QApplication::exec: Please instantiate the QApplication object first

              Christian EhrlicherC 1 Reply Last reply
              1
              • R Offline
                R Offline
                Rufledore
                wrote on last edited by
                #7

                I didn't mentioned also that it something actually turns the event loop sometimes and the signal is emitted, but not regularly. I think that it is waitForBytesWritten(). If I don't use waitForBytesWritten while writing to device nothing happens.

                1 Reply Last reply
                0
                • R Rufledore

                  Okay, now I tried it and the static way QCoreApplication::exec() doesn't block but throw : QApplication::exec: Please instantiate the QApplication object first

                  Christian EhrlicherC Online
                  Christian EhrlicherC Online
                  Christian Ehrlicher
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  @Rufledore said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

                  QApplication::exec: Please instantiate the QApplication object first

                  Then you should do this in our main() in the first line.

                  Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                  Visit the Qt Academy at https://academy.qt.io/catalog

                  R 1 Reply Last reply
                  0
                  • Christian EhrlicherC Christian Ehrlicher

                    @Rufledore said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

                    QApplication::exec: Please instantiate the QApplication object first

                    Then you should do this in our main() in the first line.

                    R Offline
                    R Offline
                    Rufledore
                    wrote on last edited by
                    #9

                    @Christian-Ehrlicher said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

                    @Rufledore said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

                    QApplication::exec: Please instantiate the QApplication object first

                    Then you should do this in our main() in the first line.

                    In principle yes, but I don't want the user of the library to bother with Qt staff as QApplication. And the main question for me is how it works okay for TCPSocket but not for SerialPort?

                    1 Reply Last reply
                    0
                    • Christian EhrlicherC Online
                      Christian EhrlicherC Online
                      Christian Ehrlicher
                      Lifetime Qt Champion
                      wrote on last edited by
                      #10

                      You need a working QCoreApplication - everything else simply works by accident and may not work suddenly.

                      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                      Visit the Qt Academy at https://academy.qt.io/catalog

                      1 Reply Last reply
                      2
                      • R Offline
                        R Offline
                        Rufledore
                        wrote on last edited by
                        #11

                        Then how to use Qt specific features as signals and slots in a library? QCoreApplication could be started only in main thread... If I start it on the background it doesn't work well. Could you give me an idea, how to proceed?

                        R 1 Reply Last reply
                        0
                        • K Offline
                          K Offline
                          kuzulis
                          Qt Champions 2020
                          wrote on last edited by kuzulis
                          #12

                          @Rufledore said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

                          Could you give me an idea, how to proceed?

                          Quick answer - nohow, it makes not sense to use Qt stuff from the non-qt libraries.

                          1 Reply Last reply
                          1
                          • Christian EhrlicherC Online
                            Christian EhrlicherC Online
                            Christian Ehrlicher
                            Lifetime Qt Champion
                            wrote on last edited by Christian Ehrlicher
                            #13

                            @Rufledore said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

                            could be started only in main thread

                            Q(Core|Gui)Application can also be started in another thread but then all gui operations must be done inside this thread too.

                            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                            Visit the Qt Academy at https://academy.qt.io/catalog

                            1 Reply Last reply
                            1
                            • R Rufledore

                              Then how to use Qt specific features as signals and slots in a library? QCoreApplication could be started only in main thread... If I start it on the background it doesn't work well. Could you give me an idea, how to proceed?

                              R Offline
                              R Offline
                              Rufledore
                              wrote on last edited by
                              #14

                              @Rufledore said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

                              Then how to use Qt specific features as signals and slots in a library? QCoreApplication could be started only in main thread... If I start it on the background it doesn't work well. Could you give me an idea, how to proceed?

                              It is a Qt library but not for Qt application :)

                              JonBJ 1 Reply Last reply
                              0
                              • R Rufledore

                                @Rufledore said in QSerialPort::readyRead() is not emitted, but QTCPSocket::readyRead() works fine.:

                                Then how to use Qt specific features as signals and slots in a library? QCoreApplication could be started only in main thread... If I start it on the background it doesn't work well. Could you give me an idea, how to proceed?

                                It is a Qt library but not for Qt application :)

                                JonBJ Online
                                JonBJ Online
                                JonB
                                wrote on last edited by JonB
                                #15

                                @Rufledore
                                I don't think you can use Qt library calls for TCP/serial port without being in a Qt application. As in, they don't work properly, as you have discovered. They need parts of the Q(Core|Gui)Application-type architecture.,

                                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