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 vs QSerialPort::waitForReadyRead
QtWS25 Last Chance

QSerialPort::readyRead vs QSerialPort::waitForReadyRead

Scheduled Pinned Locked Moved Unsolved General and Desktop
13 Posts 7 Posters 3.9k 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.
  • L Offline
    L Offline
    Linhares
    wrote on 16 Sept 2022, 14:53 last edited by
    #1

    I need to get data that is sent continually by a USB device.

    The solution I'm using now uses QSerialPort::waitForReadyRead() in a while(true) loop inside a QThread.
    However, I've read some topics (for example: https://stackoverflow.com/a/30492309) that say this is not the "good" solution. Also, this strategy is making my CPU usage jump from zero to 30% without any other processes running in the program.

    I've tried to switch to the strategy of connecting the readyRead() signal to my main function, but in this case, for an unknown reason, the program would read the data from the device just 5 times and then would stop reading.

    My questions are:
    Is it ok to use QSerialPort::waitForReadyRead() at all? If so, why is the CPU usage so high? Is there a more efficient solution for reading data from the serial port?

    Here's my code:

    MainClass::MainClass()
    {
    deviceThread *d = new deviceThread;
    
    d->start();
    
    //... other objects and connections
    }
    
    #include <QSerialPort>
    class deviceThread : public QThread
    {
        Q_OBJECT
    public:
        deviceThread();
        void run();
    
    private:
        QSerialPort *serial;
        void connectionMethod();
        void readData();
        bool connectionStatus = false;
        //... other declarations
    }
    
    deviceThread::deviceThread()
    {
    serial = new QSerialPort(this);
    // followed by serial port configuration
    }
    
    void deviceThread::run()
    {
    while(true)
        if(!connectionStatus)
            connectionMethod(); //this method works properly
        else
            readData();
    }
    
    void deviceThread::getNewData()
    {
    QByteArray newData;
    
    if(serial->waitForReadyRead())
        newData = serial->readAll();
        //etc...
    }
    
    P J 2 Replies Last reply 16 Sept 2022, 15:08
    0
    • L Linhares
      16 Sept 2022, 14:53

      I need to get data that is sent continually by a USB device.

      The solution I'm using now uses QSerialPort::waitForReadyRead() in a while(true) loop inside a QThread.
      However, I've read some topics (for example: https://stackoverflow.com/a/30492309) that say this is not the "good" solution. Also, this strategy is making my CPU usage jump from zero to 30% without any other processes running in the program.

      I've tried to switch to the strategy of connecting the readyRead() signal to my main function, but in this case, for an unknown reason, the program would read the data from the device just 5 times and then would stop reading.

      My questions are:
      Is it ok to use QSerialPort::waitForReadyRead() at all? If so, why is the CPU usage so high? Is there a more efficient solution for reading data from the serial port?

      Here's my code:

      MainClass::MainClass()
      {
      deviceThread *d = new deviceThread;
      
      d->start();
      
      //... other objects and connections
      }
      
      #include <QSerialPort>
      class deviceThread : public QThread
      {
          Q_OBJECT
      public:
          deviceThread();
          void run();
      
      private:
          QSerialPort *serial;
          void connectionMethod();
          void readData();
          bool connectionStatus = false;
          //... other declarations
      }
      
      deviceThread::deviceThread()
      {
      serial = new QSerialPort(this);
      // followed by serial port configuration
      }
      
      void deviceThread::run()
      {
      while(true)
          if(!connectionStatus)
              connectionMethod(); //this method works properly
          else
              readData();
      }
      
      void deviceThread::getNewData()
      {
      QByteArray newData;
      
      if(serial->waitForReadyRead())
          newData = serial->readAll();
          //etc...
      }
      
      P Offline
      P Offline
      Pl45m4
      wrote on 16 Sept 2022, 15:08 last edited by Pl45m4
      #2

      @Linhares said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:

      My questions are:
      Is it ok to use QSerialPort::waitForReadyRead() at all? If so, why is the CPU usage so high? Is there a more efficient solution for reading data from the serial port?

      Have a look at this example and see the description. There are two ways of dealing with the data: Asynchronous and synchronous.
      The waitForReadReady call blocks, and waits until the data comes in before you can continue,
      The async-way uses signals to notify every time data is ready without blocking anything else. So you can collect your bytes and do your further processing.
      I would recommend to use the async-approach but the blocking-thread-approach has also its use cases. Depends on your app and the rest of your code.
      For example, if you add your SP-connection to a GUI class, it would cause the GUI to freeze, if you use waitForReadReady().

      • https://doc.qt.io/qt-6/qtserialport-terminal-example.html

      Edit:

      I've tried to switch to the strategy of connecting the readyRead() signal to my main function, but in this case, for an unknown reason, the program would read the data from the device just 5 times and then would stop reading.

      Any error message in errorString()?


      If debugging is the process of removing software bugs, then programming must be the process of putting them in.

      ~E. W. Dijkstra

      1 Reply Last reply
      0
      • L Linhares
        16 Sept 2022, 14:53

        I need to get data that is sent continually by a USB device.

        The solution I'm using now uses QSerialPort::waitForReadyRead() in a while(true) loop inside a QThread.
        However, I've read some topics (for example: https://stackoverflow.com/a/30492309) that say this is not the "good" solution. Also, this strategy is making my CPU usage jump from zero to 30% without any other processes running in the program.

        I've tried to switch to the strategy of connecting the readyRead() signal to my main function, but in this case, for an unknown reason, the program would read the data from the device just 5 times and then would stop reading.

        My questions are:
        Is it ok to use QSerialPort::waitForReadyRead() at all? If so, why is the CPU usage so high? Is there a more efficient solution for reading data from the serial port?

        Here's my code:

        MainClass::MainClass()
        {
        deviceThread *d = new deviceThread;
        
        d->start();
        
        //... other objects and connections
        }
        
        #include <QSerialPort>
        class deviceThread : public QThread
        {
            Q_OBJECT
        public:
            deviceThread();
            void run();
        
        private:
            QSerialPort *serial;
            void connectionMethod();
            void readData();
            bool connectionStatus = false;
            //... other declarations
        }
        
        deviceThread::deviceThread()
        {
        serial = new QSerialPort(this);
        // followed by serial port configuration
        }
        
        void deviceThread::run()
        {
        while(true)
            if(!connectionStatus)
                connectionMethod(); //this method works properly
            else
                readData();
        }
        
        void deviceThread::getNewData()
        {
        QByteArray newData;
        
        if(serial->waitForReadyRead())
            newData = serial->readAll();
            //etc...
        }
        
        J Offline
        J Offline
        JonB
        wrote on 16 Sept 2022, 16:17 last edited by
        #3

        @Linhares said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:

        The solution I'm using now uses QSerialPort::waitForReadyRead() in a while(true) loop inside a QThread.

        This means you started with asynchronous, then turned it into synchronous, and then put that into a thread so it would run asynchronous :)

        Everything as @Pl45m4 says. waitForReadyRead() Just seems a waste for whatever your use case.

        P 1 Reply Last reply 16 Sept 2022, 16:47
        1
        • J JonB
          16 Sept 2022, 16:17

          @Linhares said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:

          The solution I'm using now uses QSerialPort::waitForReadyRead() in a while(true) loop inside a QThread.

          This means you started with asynchronous, then turned it into synchronous, and then put that into a thread so it would run asynchronous :)

          Everything as @Pl45m4 says. waitForReadyRead() Just seems a waste for whatever your use case.

          P Offline
          P Offline
          Pl45m4
          wrote on 16 Sept 2022, 16:47 last edited by Pl45m4
          #4

          @JonB

          Running waitForReadReady in a separate thread is correct. Even if you block, you want to block its own thread not any other thread. But the the thread where you use your blocking SP-connection cant be the GUI thread, of course :)

          The question is, which way fits more in @Linhares use case, since both ways got tested


          If debugging is the process of removing software bugs, then programming must be the process of putting them in.

          ~E. W. Dijkstra

          J 1 Reply Last reply 16 Sept 2022, 16:59
          0
          • P Pl45m4
            16 Sept 2022, 16:47

            @JonB

            Running waitForReadReady in a separate thread is correct. Even if you block, you want to block its own thread not any other thread. But the the thread where you use your blocking SP-connection cant be the GUI thread, of course :)

            The question is, which way fits more in @Linhares use case, since both ways got tested

            J Offline
            J Offline
            JonB
            wrote on 16 Sept 2022, 16:59 last edited by
            #5

            @Pl45m4 said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:

            Running waitForReadReady in a separate thread is correct.

            I think you misunderstand. I did not say it is not "correct". I said it is a "shame" to take an initially asynchronous call, make it synchronous and put it in a thread, when you could work with the initial asynchronous call in the first place.

            1 Reply Last reply
            4
            • K Offline
              K Offline
              Kent-Dorfman
              wrote on 19 Sept 2022, 21:21 last edited by
              #6

              For what it's worth, an idle loop to waitforReadyRead will of course work, but it breaks the framework paradigm, and therefore is discouraged.

              1 Reply Last reply
              3
              • B Offline
                B Offline
                bee65
                wrote on 21 Sept 2022, 13:25 last edited by
                #7

                @Linhares said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:

                why is the CPU usage so high? Is there a more efficient solution for reading data from the serial port?

                Sometimes it is better to use a timer, and to poll the serial port, because the device driver is able to buffer incoming data much more efficiently than a user mode process. Something like waitForReadReady() will return as soon as there is any data available. It may be only one byte. If you have a real-time requirement to respond to each byte within milliseconds of it arriving, this would be the way to do it, but you may have the overhead of a kernel and device driver switch thousands of times a second. If you are, say, updating a user interface to show the incoming data, then 1/10 second (or even 1 sec) latency may be acceptable. If you set a timer to trigger every 100ms and read all the available data each time (maybe a kbyte at a time) then the operating system overheads are much lower.

                1 Reply Last reply
                0
                • K Offline
                  K Offline
                  Kent-Dorfman
                  wrote on 21 Sept 2022, 14:30 last edited by
                  #8

                  @bee65
                  I get what you are saying but it's still a naive approach. Never use timing as an indicator of expected data on a port (at user level). Always consider serial data to be a "bursty" stream of characters, and build your string accordingly, letting the OS tell you what is available and when. This is where a solid serial protocol and skill in stream parsing becomes necessary.

                  Don't misconstrue the above to mean that you never use timers, but for normal async serial data, shouldn't be concerned about timing of character receipt.

                  and for what it's worth, when a programmer gives me a serial protocol that mentions "timing between messages" I tend to hit them with a rolled up newspaper and say "bad engineer, no cookie!"

                  1 Reply Last reply
                  0
                  • B Offline
                    B Offline
                    bee65
                    wrote on 21 Sept 2022, 20:37 last edited by
                    #9

                    OK, let me try again to answer the question

                    why is the CPU usage so high?

                    The code given may cause kernel context switching for every byte of data received.

                    Is there a more efficient solution for reading data from the serial port?

                    Use your knowledge of the latency requirements of the application to make less frequent system calls. Note that this has nothing at all to do with the timing of data arrival. For example, if you are reading serial data from GPS, you may decide you want to draw a blue dot on the screen in less than a second. If you are reading the same data for control of an autonomous vehicle, the requirement would be to process it much more quickly.

                    I've not use the QSerialPort blocking API myself, but I suggest something like this:

                    QByteArray deviceThread::newData; 
                    
                    void deviceThread::getNewData()
                    {
                      if (serial->waitForReadyRead())
                      {
                         QThread::wait(100);  
                    
                         newData = serial->readAll();
                           //etc...
                      }
                    }
                    

                    The call to QThread::wait allows some time for some incoming data to be buffered in the device driver, so that the next call to readAll() can return more data.

                    Also, you don't want to allocate and free the newData for every read. Move it to a deviceThread class member or elsewhere out of the loop.

                    The reason I would be leery of using the blocking API in a thread is it is much more complex to close it down correctly. Is it OK to call serial->close() from another thread? Does this cause waitForReadyRead() to unblock and return? I don't know...

                    1 Reply Last reply
                    0
                    • K Offline
                      K Offline
                      kuzulis
                      Qt Champions 2020
                      wrote on 22 Sept 2022, 09:05 last edited by kuzulis
                      #10

                      @bee65 ,

                      no, your suggestion has not a sense in relation to the QSP.

                      1. The QSP::readAll() does not call the system's read API, it just reads the data from the internal QSP's buffer.
                      2. The system's read does called automatically when the RX event occurred and then stores the data to the internal QSP's buffer.

                      The code given may cause kernel context switching for every byte of data received.

                      No, it is a usual way to do reading a data. An one byte or non one byte completelly depends on the device driver. If a driver is ugly implemented, then it is not a problem of QSP.

                      For example, how will you read a data from a socket? In the same place, byte-by-byte can also come. So what? ))

                      For QSP, you can reduce the context switch by getting the QSP::handle() and setting the read timeout using the system API (there you can configure this depending on the OS in different ways). But this approach will be close to a blocking, because the system's read call then will wait for a some time (or some bytes).

                      Other ways (even a polling, I mean about using the QTimer and to call the QSP::readAll() by a timeout) will not work with QSP, in that case you need to implement YourBestQSP.

                      UPD: From the other side e.g. for your custom QSP implementation , the QThread::wait(100500) can cause the driver's FIFO overflow and then you will lose a data. ))

                      J 1 Reply Last reply 22 Sept 2022, 09:11
                      1
                      • K kuzulis
                        22 Sept 2022, 09:05

                        @bee65 ,

                        no, your suggestion has not a sense in relation to the QSP.

                        1. The QSP::readAll() does not call the system's read API, it just reads the data from the internal QSP's buffer.
                        2. The system's read does called automatically when the RX event occurred and then stores the data to the internal QSP's buffer.

                        The code given may cause kernel context switching for every byte of data received.

                        No, it is a usual way to do reading a data. An one byte or non one byte completelly depends on the device driver. If a driver is ugly implemented, then it is not a problem of QSP.

                        For example, how will you read a data from a socket? In the same place, byte-by-byte can also come. So what? ))

                        For QSP, you can reduce the context switch by getting the QSP::handle() and setting the read timeout using the system API (there you can configure this depending on the OS in different ways). But this approach will be close to a blocking, because the system's read call then will wait for a some time (or some bytes).

                        Other ways (even a polling, I mean about using the QTimer and to call the QSP::readAll() by a timeout) will not work with QSP, in that case you need to implement YourBestQSP.

                        UPD: From the other side e.g. for your custom QSP implementation , the QThread::wait(100500) can cause the driver's FIFO overflow and then you will lose a data. ))

                        J Offline
                        J Offline
                        JonB
                        wrote on 22 Sept 2022, 09:11 last edited by
                        #11

                        @kuzulis
                        Thank you for this clarification. I am not a hardware person, but followed this thread for my own interest.

                        The system's read does called automatically when the RX event occurred and then stores the data to the internal QSP's buffer.

                        This is what I wondered about. Qt's readyRead() signal is being emitted without the user program issuing any kind of read...() statement. I am guessing that is tied to the underlying provider's notification that new data has arrived?

                        1 Reply Last reply
                        0
                        • K Offline
                          K Offline
                          kuzulis
                          Qt Champions 2020
                          wrote on 22 Sept 2022, 09:28 last edited by
                          #12

                          @JonB said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:

                          I am guessing that is tied to the underlying provider's notification that new data has arrived?

                          The QSP implemented in the following way:

                          1. QSP::open() does the following:
                          • opens the device by a name.
                          • gets the device descriptior.
                          • subscribes on the Rx and Tx system events on a given device descriptor.
                          1. When the Rx event occurred, then:
                          • calls the system non-blocking read call
                          • copies the read data to the internal QSP buffer.
                          • right now the QSP::bytesAvailable() makes sense (it resurns a number of bytes from the internal QSP buffer).
                          • emits the readyRead signal
                          1. The user's QSP::read() (or readAll()) reads a data only from the internal QSP buffer.

                          So, the QSP automatically collects all received data after it's opening until the user will call the QSP::read() or until the RAM reached (if the QSP's read buffer size is not set). ))

                          And yes, the QSP::readyRead() will be emitted each time when the new portion of a data arrives. And the user may call only the QSP::bytesAvailable() to see, how many bytes are ready to read by QSP::read(). Or the user may not call QSP::read() or something else, if it not want, the recceived data will not lose.

                          1 Reply Last reply
                          3
                          • J.HilkJ Offline
                            J.HilkJ Offline
                            J.Hilk
                            Moderators
                            wrote on 22 Sept 2022, 09:31 last edited by J.Hilk
                            #13

                            I'm surprised this works at all.

                            The QSerialPort instance is created in the deviceThread constructor and therefor it lives in the main GUI thread.

                            Run method may or may not actually override, who knows, as the compiler check is circumvented with the missing override identifier.

                            In case it is the actual overridden run method, you now call connect and read/write on the QSerialPort from a different thread which usually results in QObject::startTimer: timers cannot be started from another thread warnings and not running the timers

                            which would explain your high cpu uptime, as waitForReadyRead now returns immediately and you end up with essentially a while(true){} situation consuming the whole core


                            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.

                            1 Reply Last reply
                            4

                            5/13

                            16 Sept 2022, 16:59

                            8 unread
                            • Login

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