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. Delayed readReady signal of QSerialPort
Forum Updated to NodeBB v4.3 + New Features

Delayed readReady signal of QSerialPort

Scheduled Pinned Locked Moved Solved General and Desktop
14 Posts 6 Posters 2.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.
  • D Offline
    D Offline
    dazi
    wrote on last edited by
    #1

    Hi everyone

    I developed a Qt GUI to control a microcontroller based device. The communication between the PC and the device is done over a serial port with a custom protocol. The communication itself works like a charm, but there is some kind of delay between the packages I send and therefore I do not reach the desired throughput with this approach.

    I debugged the physical connection with a scope and see that the response (yellow) is returned almost immediately after sending a request (blue):
    serCom.png

    There is chain of events triggered to send and receive data and the GUI is waiting in a eventloop to complete the data transfer:

    RegGetStatusTypes IBHandler::readRegister(const quint8 devId, const quint8 regId, QByteArray &readData)
    {
        QEventLoop eLoop;
        QTimer toTimer;
    
        if (m_serialport->isOpen())
        {
            // Hookup the local eventloop
            connect(this, SIGNAL(communicationFinished()), &eLoop, SLOT(quit()));
            connect(&toTimer, SIGNAL(timeout()), &eLoop, SLOT(quit()));
    
            // Send the message
            m_txMsgRetries = 3;   // Max 3 message retries
            m_txCRCRetries = 3;
            m_txBUSYRetries = 3;
            m_msgService->slotSendMessage(devId, regId, msgRead, QByteArray());
    
            toTimer.setSingleShot(true);
            toTimer.start(500);     // Emergency timeout 500mS
            eLoop.exec();           // Start the local eventloop - Waiting for the communication to complete
    
            readData.clear();
            readData.append(m_rxMsgData);
            return m_rxStatus;
        }
        return GetPortClosed;
    }
    

    The signal/slot event chain is simplified as follows:

    1. readRegister is called in GUI
    2. triggers slotSendMessage (-> writes data to serial port) and waits in an eventloop
    3. data is received -> readyRead signal triggers data parsing slot
    4. data parsing slot triggers communicationFinished signal
    5. eventloop quits and GUI will set new data and send next request.

    I used qDebug and a elapsed timer to print timings of the events:

    ========== START ==============
    init send message
    data written to port:  0.0147 ms
    readyRead signal triggered:  15.2787 ms
    payload parsing complete:  15.3484 ms
    read transfer complete:  15.4901 ms
    ========== STOP ==============
    

    It seems that the readyRead signal is triggered 15ms after sending a request.

    Is there a way to reduce this delay?

    Any help is much appreciated.

    1 Reply Last reply
    0
    • D dazi

      The code I posted previously is using the async API only. Everything is triggered by signals and processed by slots, but the delay of the readyRead signal remains.

      aha_1980A Offline
      aha_1980A Offline
      aha_1980
      Lifetime Qt Champion
      wrote on last edited by
      #11

      @dazi Are you using a real hardware RS-232 or an USB-serial adapter like FTDI? (which one?)

      Because for the USB adapters there is a buffering involved which will delay the received data. For FTDI devices, you can reduce this delay in the advanced device settings down to one millisecond. I don't know if that's possible for other vendors.

      Regards

      Qt has to stay free or it will die.

      1 Reply Last reply
      4
      • hskoglundH Online
        hskoglundH Online
        hskoglund
        wrote on last edited by
        #2

        HI, the speed of the RS232 connection is the normal 115200 baud which means about 11520 bytes can be sent over the line per second as the best case, this translates to about 11 bytes per millisecond. So a delay of 15 milliseconds for the readyRead signal to fire is not unreasonable.

        I think you need to crank up the speed :-)

        1 Reply Last reply
        3
        • D Offline
          D Offline
          dazi
          wrote on last edited by
          #3

          Hi, thanks for your answer.
          Unfortunately cranking up the speed isn't really helpful/applicable in this setup.

          The transfer takes only 2ms, but the readyRead signal is fired 15ms later. The ratio is a bit off in my opinion... Correct me if I'm wrong but, increasing the speed would probably just reduce the transfer time, but not the readyRead signal.

          J.HilkJ 1 Reply Last reply
          0
          • D dazi

            Hi, thanks for your answer.
            Unfortunately cranking up the speed isn't really helpful/applicable in this setup.

            The transfer takes only 2ms, but the readyRead signal is fired 15ms later. The ratio is a bit off in my opinion... Correct me if I'm wrong but, increasing the speed would probably just reduce the transfer time, but not the readyRead signal.

            J.HilkJ Offline
            J.HilkJ Offline
            J.Hilk
            Moderators
            wrote on last edited by
            #4

            @dazi

            eLoop.exec(); // Start the local eventloop - Waiting for the communication to complete

            This is just wrong, I would rather you use the synchronous methods than creating and spinning your own separate event loop


            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
            • D Offline
              D Offline
              dazi
              wrote on last edited by
              #5

              Hi J.Hilk
              You are right, could have been done better. This way I prevent the GUI from freezing and return to the called line in my statemachine. However, could this eventloop cause the delay of the readyRead signal?

              jsulmJ 1 Reply Last reply
              0
              • D Offline
                D Offline
                dazi
                wrote on last edited by
                #6

                I removed the eventloop and used a signal instead_

                void IBHandler::slotRxComplete() {
                    qDebug() << "Data received: " << m_rxMsgData.toHex('|');
                    disconnect(this, SIGNAL(communicationFinished()), this, SLOT(slotRxComplete()));
                    emit sigRxComplete(m_rxStatus,m_rxMsgData);
                }
                
                RegGetStatusTypes IBHandler::readRegister(const quint8 devId, const quint8 regId)
                {
                    if (m_serialport->isOpen())
                    {
                        // Hookup the local eventloop
                        connect(this, SIGNAL(communicationFinished()), this, SLOT(slotRxComplete()));
                
                        // Send the message
                        m_txMsgRetries = 3;   // Max 3 message retries
                        m_txCRCRetries = 3;
                        m_txBUSYRetries = 3;
                        m_msgService->slotSendMessage(devId, regId, msgRead, QByteArray());
                
                        return GetSuccess;
                    }
                    return GetPortClosed;
                }
                

                Unfortunately this did not have any impact on the readyRead delay...

                1 Reply Last reply
                0
                • D dazi

                  Hi J.Hilk
                  You are right, could have been done better. This way I prevent the GUI from freezing and return to the called line in my statemachine. However, could this eventloop cause the delay of the readyRead signal?

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

                  @dazi said in Delayed readReady signal of QSerialPort:

                  I prevent the GUI from freezing

                  If you want to prevent UI from freezing then do not use synchronous API, use the asynchronous one...

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

                  1 Reply Last reply
                  2
                  • D Offline
                    D Offline
                    dazi
                    wrote on last edited by
                    #8

                    The code I posted previously is using the async API only. Everything is triggered by signals and processed by slots, but the delay of the readyRead signal remains.

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

                      I'm think, you can't improve the time. As it depends on a system scheduler, Qt-event loop and so on. Besides, the readyRead() signal triggering chain on windows is following:

                      • system RX event -> system start read FIFO -> system FIFO read complete event -> qt readyRead() signal.

                      PS: You can try to buy a more powerfull CPU. :)

                      PS2: You can look e.g. this: https://docs.microsoft.com/en-us/windows/win32/procthread/multitasking for your info.

                      PS3: Or, maybe, you can try to move your QSP instance to a separate thread and to give the 'highest' priority to that thread.

                      1 Reply Last reply
                      3
                      • D Offline
                        D Offline
                        dazi
                        wrote on last edited by dazi
                        #10

                        @kuzulis said in Delayed readReady signal of QSerialPort:

                        PS: You can try to buy a more powerfull CPU. :)

                        Well I hope an i7 core should be able to handle the serial port :)

                        I know that the PC is able to fetch data in a much higher rate. This GUI should actually replace a Labview program, which retrieves data like this (timebase of the scope is the same as in the picture above):
                        comSer2.png

                        1 Reply Last reply
                        0
                        • D dazi

                          The code I posted previously is using the async API only. Everything is triggered by signals and processed by slots, but the delay of the readyRead signal remains.

                          aha_1980A Offline
                          aha_1980A Offline
                          aha_1980
                          Lifetime Qt Champion
                          wrote on last edited by
                          #11

                          @dazi Are you using a real hardware RS-232 or an USB-serial adapter like FTDI? (which one?)

                          Because for the USB adapters there is a buffering involved which will delay the received data. For FTDI devices, you can reduce this delay in the advanced device settings down to one millisecond. I don't know if that's possible for other vendors.

                          Regards

                          Qt has to stay free or it will die.

                          1 Reply Last reply
                          4
                          • D Offline
                            D Offline
                            dazi
                            wrote on last edited by dazi
                            #12

                            @aha_1980 said in Delayed readReady signal of QSerialPort:

                            @dazi Are you using a real hardware RS-232 or an USB-serial adapter like FTDI? (which one?)

                            You are absolutly right! Thank you so much for pointing this out.

                            I'm using the FT232R USB to RS232 converter from FTDI, reduced the buffering time to 1ms and this is the result:
                            serCom3.png

                            Do you know by any chance if this settings can be changed from the application?

                            aha_1980A 1 Reply Last reply
                            2
                            • D dazi

                              @aha_1980 said in Delayed readReady signal of QSerialPort:

                              @dazi Are you using a real hardware RS-232 or an USB-serial adapter like FTDI? (which one?)

                              You are absolutly right! Thank you so much for pointing this out.

                              I'm using the FT232R USB to RS232 converter from FTDI, reduced the buffering time to 1ms and this is the result:
                              serCom3.png

                              Do you know by any chance if this settings can be changed from the application?

                              aha_1980A Offline
                              aha_1980A Offline
                              aha_1980
                              Lifetime Qt Champion
                              wrote on last edited by
                              #13

                              @dazi said in Delayed readReady signal of QSerialPort:

                              Do you know by any chance if this settings can be changed from the application?

                              I'm sorry, no; but I would be interested in this answer also :) So if you find a solution, feel free to share.

                              Regards

                              Qt has to stay free or it will die.

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

                                but I would be interested in this answer also :)

                                As I remember, we did some project where we do changes for FTDI latency parameter:

                                #ifdef Q_OS_WIN
                                // Возвращает указатель реестра для устройства с заданным именем,
                                // если оно является последовательным портом FTDI, либо 0.
                                HKEY openFtdiDeviceParameters(const QString &portName)
                                {
                                    const wchar_t *keypath = L"SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS";
                                    HKEY key;
                                    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keypath, 0, KEY_READ, &key)) {
                                        // Порты FTDI отсутствуют.
                                        return 0;
                                    }
                                
                                    // Просматриваем все порты FTDI.
                                    int idx = 0;
                                    HKEY readOnlyKey = 0;
                                    HKEY paramKey = 0;
                                    while (true) {
                                        if (paramKey) {
                                            RegCloseKey(paramKey);
                                            paramKey = 0;
                                        }
                                        DWORD subkeySize = 140;
                                        wchar_t subKey[140] = {0};
                                        // Последовательный порт не является устройством FTDI.
                                        if (RegEnumKeyEx(key, idx++, subKey, &subkeySize, 0, 0, 0, 0)) {
                                            break;
                                        }
                                        QString paramPath(QString(QLatin1String("%1\\0000\\Device Parameters")).arg(QString::fromWCharArray(subKey)));
                                        if (RegOpenKeyEx(key, paramPath.toStdWString().c_str(), 0, KEY_READ, &readOnlyKey)) {
                                            continue;
                                        }
                                        DWORD regPortNameSize = 20;
                                        wchar_t regPortName[20] = {0};
                                        DWORD type = 0;
                                        LONG result = RegQueryValueEx(readOnlyKey, L"PortName", 0, &type, (LPBYTE) &regPortName, &regPortNameSize);
                                        if (ERROR_SUCCESS == result) {
                                            QString name = QString::fromWCharArray(regPortName);
                                            if (portName == name) {
                                                RegOpenKeyEx(readOnlyKey, 0, 0, KEY_READ | KEY_SET_VALUE, &paramKey);
                                            }
                                        }
                                        RegCloseKey(readOnlyKey);
                                        if (paramKey) {
                                            break;
                                        }
                                    }
                                    RegCloseKey(key);
                                    return paramKey;
                                }
                                #endif
                                

                                and then:

                                #ifdef Q_OS_WIN
                                    // Если выбранный порт является FTDI, устанавливаем его latency в 1.
                                    DWORD latency = 0;
                                    HKEY latencyKey = openFtdiDeviceParameters(name);
                                    DWORD dwType = 0;
                                    DWORD dwSize = sizeof(DWORD);
                                    bool foundFtdiDevice;
                                    if (latencyKey && !RegQueryValueEx(latencyKey, L"LatencyTimer", 0, &dwType, (LPBYTE) &latency, &dwSize)
                                        && dwType == REG_DWORD) {
                                        foundFtdiDevice = true;
                                    } else {
                                        foundFtdiDevice = false;
                                    }
                                
                                    if (foundFtdiDevice) {
                                        if (latency != 1) {
                                            DWORD dwLatency = 1;
                                            RegSetValueEx(latencyKey, L"LatencyTimer", 0, REG_DWORD, (LPBYTE) &dwLatency, sizeof(DWORD));
                                            RegCloseKey(latencyKey);
                                        }
                                    }
                                
                                #endif
                                

                                Here we did set the latency to 1 msec.

                                PS: Sorry, all comments in russian (because it is copy-paste).

                                1 Reply Last reply
                                1

                                • Login

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