I was forced to write some code in Qt for a sensor to integrate with existing Qt code. Otherwise I have tested the sensor data read with very simple Python code using the pyserial library, and it works like a charm. Unfortunately, I got involuntarily dragged into the complicated quagmire that is Qt framework.
Hardware: Sensor plugged into a USB port on Raspberry Pi 4 through a USB-to-Serial adapter.
Sensor is configured to output every 1sec, one line of RS-232 data as ASCII characters ending in a '\r' (CR, ASCII 13), not '\n' (LF, ASCII 10).
This is my code:
#include <QCoreApplication>
#include <QThread>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/qserialportinfo.h>
#include <QList>
#include <QString>
#include <stdio.h>
#include <iostream>
#include <QtCore/QDataStream>
static QString sensor = "";
static QList<QSerialPortInfo> portList;
static QSerialPortInfo sensorport;
static QSerialPort s_sensor;
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
portList = QSerialPortInfo::availablePorts();
int size = portList.size();
const auto portnames = portList[1];
std::cout << "Number of ports: " << size << "\n";
for(int i=0;i<size;i++)
{
std::cout << portList[i].portName().toStdString() << " " << portList[i].description().toStdString() << "\n";
if(portList[i].portName().toStdString()=="ttyUSB0"&&portList[i].description().toStdString()=="USB-Serial Controller D")
{
sensor.append(portList[i].portName());
sensorport = portList[i];
}
}
std::cout << sensor.toStdString() << "\n";
std::cout << sensorport.manufacturer().toStdString() << "\n";
s_sensor.setPort(sensorport);
std::cout << s_sensor.open(QIODevice::ReadOnly) << "\n";
std::cout << s_sensor.portName().toStdString() << "\n";
std::cout << s_sensor.parity() << "\n";
std::cout << s_sensor.baudRate() << "\n";
std::cout << s_sensor.dataBits() << "\n";
std::cout << s_sensor.stopBits() << "\n";
std::cout << s_sensor.isOpen() << "\n";
//std::cout << s_sensor.canReadLine() << "\n";
//char buf[1024];
//QString buff;
static QByteArray byteArray;
while(s_sensor.isOpen())
{
//QByteArray byteArray;
if(s_sensor.waitForReadyRead())
{
byteArray += s_sensor.readAll();
}
if(QString(byteArray).contains('\r'))
{
QString data = QString(byteArray);//.remove("*");
byteArray.clear();
std::cout << "End of line\n";
std::cout << data.toStdString() << "\n";
}
else
{
//std::cout << "None\n";
}
// if(s_sensor.waitForReadyRead() && s_sensor.bytesAvailable()>=18)
// {
// //std::string ll = s_sensor.readAll().toStdString();
// //std::cout << ll << "\n";
// qint64 len = s_sensor.readLine(buf,1024);
// //QString bb = s_sensor.readAll();
// std::cout << "Num of bytes: " << len << "\n";
// //std::cout << "Num of bytes: " << sizeof(bb) << "\n";
// std::cout << buf << "\n";
// //std::cout << bb.toStdString() << "\n";
// std::cout << "inner Yes" << "\n";
// //break;
// }
//buff = buf;
//std::cout << buff.toStdString() << "\n";
//std::cout << "outer Yes" << "\n";
//qint64 windline = s_sensor.readLine(buf, sizeof(buf));
}
//std::cout << buff.toStdString() << "\n";
return app.exec();
}
The code shows the layers of many successive trials, as seen in the many different commented lines of code.
The code as it is currently, does not run. After the project has been built, the Application Output window displays "Starting (...) ..." forever. This was the result of adding these:
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv)
return app.exec();
}
Adding these lines was done to get rid of a weird warning "QSocketNotifier: Can only be used with threads started with QThread". After extensive searching online, I found an answer on SO that said to include QCoreApplication to create an event loop. Well, adding these lines certainly got rid of the weird warning, but now my program doesn't even start.
Prior to the above step, I only had the following:
int main()
{
//QCoreApplication app(argc, argv)
//return app.exec();
}
I kept getting the weird warning, but I just kept ignoring it. But on the other hand, my program ran successfully.
However, there was still a long delay before I got any output. The typical wait after the build was around 2-3 minutes!
Whereas with my simple Python code using pyserial, I get output immediately after compile, and each line of output is perfectly synchronized with the 1 sec output interval of the sensor. This is the way it should ideally work.
I tried all combinations of readLine()
and readAll()
. There is no option to specify the end-of-line character. The default is '\n'. I finally found two alternate ways to read each line of data. You can see the code for both ways above. The first way is I continuously add new incoming characters into byteArray
and check for '\r' character. The second way is I specify the byte size of a line of data and read it.
I don't know if I'm doing something fundamentally wrong here that is causing the long delay in execution. After the initial 3 minute delay, a whole bunch of data is spat out and then it continues spitting out data, but not in any way synchronized with 1 sec intervals.