QSerialPort's read buffer is always empty while running another func.



  • Hi everyone,
    I have a problem related QSerial port . I send the back-to-back byte from mcu. Unless I wait a event or run while/for loop, everything is normal. But if I have a loop function, then serialport buffer is always empty and after this function ended, everything is normal running again. While reading data, I use the readAll() function.

    Reading function : connect(serial, &QSerialPort::readyRead, this, &serial_port::readData);

    void serial_port::readData()
    {
      int NumberOfBytesToRead = serial->bytesAvailable();
      qDebug()<<"NumberOfBytesToRead..."+ QString::number(NumberOfBytesToRead);
      newdata.append(serial->readAll());
    }
    

    And I just tought that maybe it can be related signal and slots. So, I have replaced it with real time system , Qthread. However, the number of bytes is always zero. when it happens the new event of qthread which is 1 milisec during another loop function is running.


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Where are you calling these loops ?

    Please share your non working code.


  • Moderators

    @Gokhan In these "loops" do you allow events to be processed? If not that is your issue. If you lock up execution in a loop for your event processing thread than it can't handle events which includes serial events.

    If you could show us the code we could tell for sure, but that is probably the issue based off your description of the problem.



  • Well, there are two classes in my project, one is GUI class and a form application, other is serial_port class and it makes the opening, closing, receiving, writing etc. about the port, briefly this class provides the communication between PC and MCU through the serial port.

    When the Gui application starts, the serial port's threat immediately runs for a millisecond and it calls serial_port :: readData () is in my above message via Qtconcurrent function in run function. So I can read buffer every a millisecond.

    By the way, the GUI window has a few button in order to make process with data. For example, one provides for decoding the data which have just received from the port. Other provides for starting the windows console (cmd.exe) and makes something about the application via it. If this event takes a long time (e.g. proc.waitReadyRead function takes a long time sometimes.), then the serial port thread always shows the read buffer to be empty. However, the thread normally continues the running for a millisecond. (I use the clicked event for these buttons.)


  • Moderators

    @Gokhan I don't see anything in what you said that let me figure out what's going on. You'll have to share the relevant parts of your code in order for us to help you more I think.

    Also, side note on threading.. Running a thread every 1ms is too aggressive imo. Most schedulers can't even swap threads out that fast. So your thread is basically at 100% run time. If you actually want to let the CPU rest for that thread you need to lower that time. I find I never go below 100ms for thread sleeps as that tends to be near the edge of context switching by most CPUs.

    The other potential of a 1ms sleep is that you are actually getting much more than 1ms. Again this is because the thread context swaps can't happen that fast.

    That method of threading is called polling and it isn't a good way to do things in the modern world. Qt provides signals for when there is data to be read from the QIODevice. If you handle this signal, you don't even need a timeout to kickoff the thread.



  • @ambershark Many thanks for your reply.
    I can't share all codes because they are at office's PC. I'm wondering a thing what you said about the signal method. If I also don't use thread method to read the buffer, can I all read the data only a signal method without missing?

    In addition to, e.g I'm using the console (cmd.exe) in order to move folder is huge size. After I started cmd code to move it somewhere, it immediately has the finished event. However, I want it to give me an event after it finished the data transfer. So, I can use any signals method to do it? e.g can readyReadStandardOutput() be? I guess that if I can use this method, it will be the good way to do this like you have mentioned above. Isn't it?


  • Lifetime Qt Champion

    Can you show the code you use for that ?



  • I have just checked for a new application and have the same problem. e.g. I'm using an external cmd.exe file to convert the images to bytes. I start the first conversion via a button.

    void image_down_dialog::on_convertButton_clicked()
    {
        QStringList arguments;
        QString com=QString("/k img_cvt  -i  %1   -f   0").arg(img_down[0].image_with_ext);
        arguments <<com;
        QDir::setCurrent("D://conv");
        QProcess aa ;
    
        aa.start("cmd.exe",arguments);
        proc = &aa;
    
        connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)),
                this, SLOT(finishProcess(int, QProcess::ExitStatus)) );
    }
    
    void image_down_dialog::finishProcess ( int exitCode, QProcess::ExitStatus exitStatus )
    {
      QString com=QString("/k img_cvt  -i  %1   -f   0").arg(img_down[idxProcess].image_with_ext);
      arguments <<com;
      QDir dir;
      dir.setCurrent("D:/conv");
      QProcess proc;
      proc.start("cmd.exe",arguments);
    
       connect(&proc, SIGNAL(finished(int, QProcess::ExitStatus)),
                    this, SLOT(finishProcess(int, QProcess::ExitStatus)) );
    
    //I write proc.waitForFinished(100) to here ..
    }
    

    After I start the conversion, the process immediately gives me the finished event. Actually, it's still running, not finished. However, if I use "proc.waitForFinished(100);" in the finished event, then it normally completes them. I have to give 100 milliseconds to wait for finished otherwise, it can't finish again.



  • @Gokhan

    mmh maybe an error occurred during exe? that would probably result in a finished Signal.

    What does qDebug() say, if you write something like this?

    QString com=QString("/k img_cvt  -i  %1   -f   0").arg(img_down[idxProcess].image_with_ext);
      arguments <<com;
      QDir dir;
      dir.setCurrent("D:/conv");
      QProcess proc;
    
       connect(&proc, & QProcess::errorOccurred, &proc,[=](QProcess::ProcessError error){qDebug() <<"Process had an error:" <<  error; });
       connect(&proc, SIGNAL(finished(int, QProcess::ExitStatus)),
                    this, SLOT(finishProcess(int, QProcess::ExitStatus)) );
       proc.start("cmd.exe",arguments);
    

  • Lifetime Qt Champion

    In both your methods, your QProcess objects are local on the stack. They'll be destroyed at the end of the methods and will likely not have finished at that point.

    Also, you argument list doesn't look right, you are trying to put everything on one line. You should put each element separately in the QStringList object.



  • @J-Hilk Yes. it gives the "Crashed" error and "Destroyed while process ("cmd.exe") is still running." What can I do to solve this?
    @SGaist I know they are a local variable, I can't use a global variable. I tried to use a global variable instead of them, this time it never gave the finished event. I guess I didn't completely connect this signal. Also, this argument list is running like I want, if I write the waiting function...


  • Lifetime Qt Champion

    There's no need for global variables. Since you'll be using these commands several times, just keep them as member variables and setup them once in your dialog constructor.

    Then you can re-use them as needed.



  • @Gokhan said in QSerialPort's read buffer is always empty while running another func.:

    @J-Hilk Yes. it gives the "Crashed" error and "Destroyed while process ("cmd.exe") is still running." What can I do to solve this?
    @SGaist I know they are a local variable, I can't use a global variable. I tried to use a global variable instead of them, this time it never gave the finished event. I guess I didn't completely connect this signal. Also, this argument list is running like I want, if I write the waiting function...

    Actually, that are to be expected error messages, for reasons, @SGaist explained!

    QProgress does not have to be global to work e.g:

    QString com=QString("/k img_cvt  -i  %1   -f   0").arg(img_down[idxProcess].image_with_ext);
      arguments <<com;
      QDir dir;
      dir.setCurrent("D:/conv");
      QProcess *proc = new QProcess();
    
       connect(proc, & QProcess::errorOccurred, proc,[=](QProcess::ProcessError error){qDebug() <<"Process had an error:" <<  error; });
       connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)),
                    this, SLOT(finishProcess(int, QProcess::ExitStatus)) );
      connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), proc, SLOT(deleteLater()));
       proc->start("cmd.exe",arguments);
    


  • @SGaist and @J-Hilk many thank your reply.
    I'm using the proc variable as a member variable that is decelerated and initialized in class. "QProcess *proc = new QProcess();" like you said. However, it never gave the finish event. Why don't I get any event?
    And when I re-click the button, it gives an error that is "QProcess::start: Process is already running",

    void image_down_dialog::on_convertButton_clicked()
    {
        idxProcess = 0;
        find_all_imgs();
    
        QStringList arguments;
        QString com=QString("/k img_cvt  -i  %1   -f   0").arg(img_down[0].image_with_ext);
        arguments <<com;
        QDir::setCurrent("D:/User Interface/GUI_660_HMI/conv");
    
        connect(proc, & QProcess::errorOccurred, proc,[=](QProcess::ProcessError error){qDebug() <<"Process had an error:" <<  error; });
        connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)),
                this, SLOT(finishProcess(int, QProcess::ExitStatus)) );
        connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), proc, SLOT(deleteLater()));
    
        proc->start("cmd.exe",arguments);
    }
    


  • @Gokhan
    ok first, if you make QProcess a member variable, move the connects into the constructor:

    QProcess *proc = new QProcess();
    connect(proc, & QProcess::errorOccurred, proc,[=](QProcess::ProcessError error){qDebug() <<"Process had an error:" <<  error; });
    connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)),
                this, SLOT(finishProcess(int, QProcess::ExitStatus)) );
    

    or you get problems when you press the button more than once.

    also the deleteLater has to be moved to the destructor or you can use your QProcess object only once.

    ~MyClass(){
       proc->deleteLater();
    }
    

    Why don't I get any event?
    And when I re-click the button, it gives an error that is "QProcess::start: Process is already running",

    it would seem your process is still running right?



  • @J.Hilk said in QSerialPort's read buffer is always empty while running another func.:

    also the deleteLater has to be moved to the destructor

    If you pass the parent in the constructor there is no need to manually do this



  • @VRonin true, but @Gokhan said

    I'm using the proc variable as a member variable that is decelerated and initialized in class. "QProcess *proc = new QProcess();"

    So in this particular case the deleteLater() is needed. More convenient would of course be to give QProcess a parent.



  • @J-Hilk I did them you said, but the result is the same. It never gives the finished event, so it is always running.


  • Moderators

    @Gokhan Please read again what @SGaist wrote: each argument should be in its own string. You, instead, put all parameters into one string.



  • @jsulm "/k img_cvt -i %1 -f 0" this is a one line command, so it has to write in one line. While I was also manually using this cmd application before, using it the same. Also, it successfully completes the conversion and creates a new folder within hex code. However, it never gives the finished information, it is continuous running to finish. I can see it's still running when re-click the button.


  • Moderators

    @Gokhan said in QSerialPort's read buffer is always empty while running another func.:

    /k img_cvt -i %1 -f 0

    Wrong it consists of several parameters: "/k" "img_cvt" "-i" "%1" "-f" "0"



  • That is a part of the user manual. I don't know how to use it like you said? However, it's normally running if use the waitforfinish function like I have mentioned above.

    The usage is : 
        img_cvt  -i  inputfilename  -f  format
    
             format is as follow:
                0 : ARGB1555 [default]
                1 : L1
                2 : L4
                3 : L8
                4 : RGB332
                5 : ARGB2
                6 : ARGB4
                7 : RGB565
                8 : PALETTEED [FT80X only]    
                9 : L2 [FT81X only]
    
    Example : 
        img_cvt  -i  lenaface40.png  -f   8
    
    

  • Moderators

    @Gokhan
    Just pass each parameter as one string in the string list (as shown here http://doc.qt.io/qt-5/qprocess.html):

    arguments <<"/k"<<"img_cvt"<<"-i"<<QString(%1).arg(img_down[0].image_with_ext)<<"-f"<<"0";
    

    Also you should connect http://doc.qt.io/qt-5/qprocess.html#errorOccurred signal to a slot to check whether something went wrong.



  • @jsulm I just changed my code like you said and connected the signals that are like below in the constructor. The issue isn't solved. ErrorOccurred don't give an error while running.

        proc = new QProcess ();
        connect(proc, & QProcess::errorOccurred, proc,[=](QProcess::ProcessError error){qDebug() <<"Process had an error:" <<  error; });
        connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)),
                    this, SLOT(finishProcess(int, QProcess::ExitStatus)) );
    

  • Moderators



  • @jsulm There is no any readAllStandardError and you can see the output of readAllStandardOutput below. Everything shows normal.

    readyReadStandardOutput "image conversion utility for FT8XX V0.7\r\nconvert complete!\r\n"
    readyReadStandardOutput "\r\nD:I\\conv>"
    


  • @jsulm @J-Hilk and @SGaist

    I'm trying that it, but I get an error that is "no matching function for call to 'image_down_dialog::connect(QProcess*&, <unresolved overloaded function type>, image_down_dialog*, void (image_down_dialog::*)(int, QProcess::ExitStatus))' " Why do I get this?
    ^
    connect(proc, &QProcess::finished, this, &image_down_dialog::finishProcess);


  • Lifetime Qt Champion

    How did you declare proc?



  • @Gokhan
    Sadly, Signal finished is overloaded in this class. To connect to this one using the function pointer syntax, you must specify the signal type in a static cast:

    connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), [=](int exitCode, QProcess::ExitStatus exitStatus){ /* ... */ });
    


  • @J-Hilk It should be connected below to compile successfully, so it's static cast. I did it and it can compile and connect now without an issue. However, I haven't solved this problem yet, it isn't still giving the finished event. Can it be a bug with qprocess ? Should I write a bug report?
    QObject::connect(proc, (void (QProcess::*)(int,QProcess::ExitStatus))&QProcess::finished, this, &image_down_dialog::finished);


  • Lifetime Qt Champion

    You should rather use qOverload. In the case you can't, use a static_cast like @J-Hilk shown, not a C-Style cast.

    As for never finishing, maybe because cmd.exe is still running. You are passing the /k parameter to cmd.exe which AFAIK means run the command and return to the CMD prompt. Shouldn't you be using /C ?



  • @SGaist thank you. The problem was solved when changed "/k" with "/c". However, the problem which I have mentioned in the title hasn't been solved yet. I'm still missing some data during communication with MCU through the serial port. The missing data are around 3K while sending 300K.

    Also, I able to read max 512 byte in the readyRead signal, Is it writing to its read buffer during making another process like filling a table? If yes, how can I read the all buffer without missing any data?


  • Lifetime Qt Champion

    Do you have any protocol established for the communication ?



  • @SGaist I have just solved this problem. it was caused by debugging mode. After it was realesed and run .exe, it normally ran, until the application window is moved. Yes, I have a new problem :). It's lagging and missing data during moving or resizing the application window. I just tried to up to real time priority for windows on task manager, but it's still missing.



  • @Gokhan
    how about moving your QSerialPort stuff into int's own thread? Should make things a bit smoother as in its not blocked when your main thread is busy with ui-stuff.



  • @J-Hilk I have already connected to a function and read it in this function. I don't make anything other this, it only reads the serial port and converts the data. Until moving the window, I tried it for around an hour, it never misses data. Actually, not only moving, it also has this problem during two-clicking the window's border.



  • @Gokhan
    never the less you should seperate your QSerialPort interaction from your GUI-Stuff. If a rather empty GUI-Resize causes you to lose data, imagine what will happen if your programm eventually becomes more complex.

    From your description its is critical that no informations are lost. So make a workerclass for your SerialPort interaction and run it in its own thread. Eventually raise that thread priority too.

    And you should be fine. Its easy enough to do with qt.

    Here a link that will help:



  • @J-Hilk OK. I have to use a Qthread and read buffer with it. So, how should I read the buffer? Which registers do I check before reading? How do I able to read periodically? For example, I'm using the following codes, but it can't connect the serial port.
    The process function is the same with the link.

        QThread* thread = new QThread;
        serialPort.moveToThread(thread);
        connect(&serialPort, SIGNAL (error(QString)), this, SLOT (errorString(QString)));
        connect(thread, SIGNAL (started()), &serialPort, SLOT (process()));
        connect(&serialPort, SIGNAL (finished()), thread, SLOT (quit()));
        connect(&serialPort, SIGNAL (finished()), &serialPort, SLOT (deleteLater()));
        connect(thread, SIGNAL (finished()), thread, SLOT (deleteLater()));
        thread->start();
    
    void serial_port::process()
    {
        for(;;)
        {
            if(serial->isOpen())
            {
                msleep(100);
                if (serial->waitForReadyRead(100))
                {
                    // read request
                    newdata.append(serial->readAll());
                    while (serial->waitForReadyRead(10))
                        newdata.append(serial->readAll());
                }
            }
        }
    }
    


  • @Gokhan

    ok,

    same basic info.
    The constructor, of your serial port class, should be empty. Everything necessery should be initialized after the thread is started. That includes creating the instance of the QSerialPort as well as all connections:

    //serialPort class
    
    void serialPort::init(){
        serial = new QSerialPort();
        connect(serial, &QSerialPort::readyRead, this, serialPort::newData);
    
    }
    
    void serialPort::newData{
       QByteArray bArray = serial->readAll();
       //do Stuff
    ....
       emit finishedData(data);
    }
    

    QSerialPort should emit the readyRead Signal when ever it has new data to be processed. Just connect to signal to a slot and handle the data there. -> The Thread will be more or less idle - only running its event loop- but still react as soon as new data is available.

    the thread handling in your MainClass seems good enough:

    QThread* thread = new QThread;
    serialPort *mySerialPort = new serialPort();
    mySerialPort -> moveToThread(thread);
        
    connect(mySerialPort , SIGNAL (error(QString)), this, SLOT (errorString(QString)));
    connect(mySerialPort , SIGNAL (finishedData(QVariant)), this, SLOT (displayData(QVariant)));
    connect(thread, SIGNAL (started()), mySerialPort , SLOT (init()));
    connect(mySerialPort , SIGNAL (finished()), thread, SLOT (quit()));
    connect(mySerialPort , SIGNAL (finished()), &serialPort, SLOT (deleteLater()));
    connect(thread, SIGNAL (finished()), thread, SLOT (deleteLater()));
    thread->start();
    


  • @J-Hilk I have just changed like you said, but it hasn't been solved again. It continues to miss some data during resizing or moving windows.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.