QTSerialPort Read/Write doesn't properly work on Windows
-
Hello, I'm trying to write a code for my application. My goal is to write a bunch of bits to the serial port and in this time read the serial port for a reply from the connected device. While I'm writing to the serial port and reading from the serial port, also I want to measure elapsed time. I'm not really experience with QT and I'm facing with some problems.
I started from a basic application which read serial port, but it's not working as it's supposed to work. I tested my code in Linux machine, it's work flowless, but in windows, it's not reading serial port.
In Linux, when I run the code, it listens until i close the program. Whatever I sent from serial port, It directly print to the terminal. But in windows, it just waits and doesn't read anything. I also set my buffer to smaller but that's also didn't work. But when I uncomment that //selectedPort->waitForReadyRead(5000); part in windows, It works and read serial port for 5 sec. Then it's idle without reading anything.
On the other hand, I want to measure the time elapsed during writing and reading. I'm trying to write 1MB of data, with 115200baudrate which is around 11,25kb/sec as my calculation. But when I try with my code it returns 1-2ms which seems wrong. I created 16kb of data with a for-loop. I suppose to take more than 1sec but my code returns 0ms. I think, I don't know how to make it properly. Also i need this kind of stuff also for reading.
I'm looking for any kind of suggestion for my application. Sorry for my mistakes.
myserial.cpp
MySerial::MySerial(QString portn, uint32_t baudrate){ selectedPort = new QSerialPort; this->baudrate = baudrate; selectedPort->setPortName(portn); selectedPort->setBaudRate(static_cast<qint32>(baudrate)); selectedPort->open(QIODevice::ReadWrite); connect(selectedPort, SIGNAL(readyRead()),this, SLOT(MyReadyRead())); //selectedPort->waitForReadyRead(5000); } void MySerial::MyReadyRead() { QByteArray myData = selectedPort->readAll(); std::cout << myData.toStdString();fflush(stdout); } void MySerial::startWrite(QByteArray myData){ myTime.start(); for(long i=0; i<1024;i++){ // 16byte -> 16kb selectedPort->write(myData); } std::cout << "Time elapsed(in ms):" <<myTime.elapsed()<<std::endl; }
main.cpp
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MySerial *s0 = new MySerial("COM6",115200); QByteArray testData = "1111111111111111";// // 16 char = 16byte of data s0->startWrite(testData); return a.exec(); }
-
Hi and welcome to devnet,
You should check whether your open call is successful. Then you should also check for errors in your code.
Are you sure that you are opening the correct com port ?
-
@canerbldk and to add to @sgaist, which Qt version are you using? there has been a bug recently which influenced the receiving of characters.
Regards
-
@SGaist with //selectedPort->waitForReadyRead(5000); i'm able to read correctly and i also tested my open function seems correct.
if(selectedPort->open(QIODevice::ReadWrite)) std::cout << "Port " << portn.toStdString() << "is open" << std::endl;
@aha_1980 I'm using Qt 5.13.1 (MSVC 2017, 32 bit)
I also created 2 virtual serial comm. port to test everything. I connected one port to Docklight and the other one to my code, I received and delivered everything correctly.
-
std::cout << myData.toStdString();fflush(stdout);
Why use
fflush(stdout)
, are you surestd::cout
is using the stdout buffer like that? Prefercout.flush(); // or cout << ... << flush;
Next:
InstartWrite()
you timeQSerialPort::write()
operations. I am not an expert on serial I/O, but in general that returns immediately only guaranteeing to queue the output, not execute it. You have signals likeQIODevice::bytesWritten
or functionQSerialPort::waitForBytesWritten()
for that. Also you do not check the return result fromQSerialPort::write()
, it might be returning less bytes than you sent or even -1 for error. And you don't have any calls toQSerialPort::flush()
or handler forQSerialPort::errorOccurred
, just in case. @SGaist mentioned this.The above two might behave differently on Windows vs Linux, so worth just checking.
-
@JonB I modified my code a bit as much as what I understood. I added an error handler and I modified fflush(stdout). Nothing changed. I don't get any error. I'm still not able to read without //selectedPort->waitForReadyRead(5000);. Also, I have the same problem for bytesWritten. I have to add //selectedPort->waitForBytesWritten(3000); to trigger MyBytesWritten() function.
On the other hand, although I run the same code twice, I'm getting different outputs, in terms of elapsed time. My guess, I'm measuring data write time to write-buffer, instead of write time to the serial port.
I still need some help to figure out how to measure the time elapsed correctly. Thank you for your help.
main.cpp
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MySerial *s0 = new MySerial("COM6",115200); QByteArray testData = "1111111111111111";// // 16 char = 16byte of data s0->startWrite(testData); s0->startListen(); s0->startWrite("C"); return a.exec(); }
myserial.cpp
#include "myserial.h" #include <stdio.h> #include <iostream> #include <unistd.h> #include <QCoreApplication> #include <QObject> MySerial::MySerial(QString portn, uint32_t baudrate) { selectedPort = new QSerialPort; this->baudrate = baudrate; selectedPort->setPortName(portn); selectedPort->setBaudRate(static_cast<qint32>(baudrate)); if(selectedPort->open(QIODevice::ReadWrite)) std::cout << "Port " << portn.toStdString() << " is open" << std::endl; connect(selectedPort, &QSerialPort::errorOccurred, this, &MySerial::handleError); connect(selectedPort, SIGNAL(readyRead()),this, SLOT(MyReadyRead())); connect(selectedPort,SIGNAL(bytesWritten(qint64)),this,SLOT(MyBytesWritten(qint64))); myTime.start(); } void MySerial::MyReadyRead() { QByteArray myData = selectedPort->readAll(); std::cout << myData.toStdString(); std::cout.flush()<<std::endl; } void MySerial::MyBytesWritten(qint64){ selectedPort->flush(); std::cout << "MyBytesWritten(in ms):" <<myTime.elapsed()<<std::endl; } MySerial::~MySerial(){ selectedPort->close(); selectedPort->disconnect(); } void MySerial::startWrite(QByteArray myData){ std::cout << "Time elapsed(in ms):" <<myTime.elapsed()<<std::endl; myTime.restart(); for(long i=0; i<1024;i++){ // byte -> kilobyte selectedPort->write(myData); } selectedPort->waitForBytesWritten(3000); myTime.restart(); } void MySerial::startListen() { while(selectedPort->waitForReadyRead(3000)){ std::cout << "Read Data" <<myTime.elapsed()<<std::endl; MyReadyRead(); } } void MySerial::handleError(QSerialPort::SerialPortError serialPortError) { if (serialPortError == QSerialPort::ReadError) { m_standardOutput << QObject::tr("An I/O error occurred while reading " "the data from port %1, error: %2") .arg(selectedPort->portName()) .arg(selectedPort->errorString()) << endl; QCoreApplication::exit(1); } }
output1
Port COM6 is open Time elapsed(in ms):0 MyBytesWritten(in ms):606 ----o Ping ----o Ping Time elapsed(in ms):3001 MyBytesWritten(in ms):27
output2
Port COM6 is open Time elapsed(in ms):0 MyBytesWritten(in ms):644 ----o Ping ----o Ping Time elapsed(in ms):3001 MyBytesWritten(in ms):28
-
@canerbldk said in QTSerialPort Read/Write doesn't properly work on Windows:
I'm using Qt 5.13.1 (MSVC 2017, 32 bit)
jepp, IIRC that's one of the versions where the signals are not emitted correctly.
5.13.2 should be out and it should work there, if I'm not mistaken. @aha_1980 should be able to correct me if I'm wrong :) -
-
@J-Hilk OH my god! I'm trying to fix that problem for a week. I tried lots of things to understand why it's not working correctly on windows, but I never think about QT version :( Thanks a lot for your help !!!
But, I'm still looking for a suggestion for read-write timing measurement. As I stated before I'm thinking that I'm measuring write-to-buffer time while I need write-to-serial timing. Thanks in advance
-
@canerbldk said in QTSerialPort Read/Write doesn't properly work on Windows:
As I stated before I'm thinking that I'm measuring write-to-buffer time while I need write-to-serial timing.
That is hardly possible, as there are many buffers involved: in your app, in Qt, in the OS, in the driver, ...
What you can do is to time the round trip i.e. send a command and measure the time until the answer comes back.
Regards