Serial message received partially - c++

I try to receive serial message from device, it seems like readyRead() signal is activated after 1, 2 or more chars that appeared on serial buffer.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
timerTx = new QTimer(this);
timerTx->setInterval(100);
timer.start();
serialRx = new QSerialPort(this);
serialRx->setPortName("COM5");
serialRx->setBaudRate(QSerialPort::Baud9600);
serialRx->setParity(QSerialPort::NoParity);
serialRx->setDataBits(QSerialPort::Data8);
serialRx->setStopBits(QSerialPort::OneStop);
serialRx->setFlowControl(QSerialPort::NoFlowControl);
serialTx = new QSerialPort(this);
serialTx->setPortName("COM3");
serialTx->setBaudRate(QSerialPort::Baud9600);
serialTx->setParity(QSerialPort::NoParity);
serialTx->setDataBits(QSerialPort::Data8);
serialTx->setStopBits(QSerialPort::OneStop);
serialTx->setFlowControl(QSerialPort::NoFlowControl);
connect(serialRx, SIGNAL(readyRead()), this, SLOT(serialReceive()));
connect(timerTx, SIGNAL(timeout()), this, SLOT(serialSend()));
connect(ui->pushButton, SIGNAL(clicked()), timerTx, SLOT(start()));
}
void MainWindow::serialReceive()
{
QByteArray baRx, num, numOfMs;
qint64 time_ms;
baRx = serialRx -> readAll();
qDebug() << baRx;
time_ms = timer.elapsed();
counterRecDev++;
num = QByteArray::number(counterRecDev);
numOfMs = QByteArray::number(time_ms);
ui->receiveWindow->insertPlainText(num + "\t" + baRx + " \t " + numOfMs +
"\n" );
ui->receiveWindow -> moveCursor(QTextCursor::End);
}
I send "#Test$" message via serial port but I receive that:
"#"
"T"
"est"
"%"
other time:
"#"
"T"
"es"
"t"
"%"
Could you tell me please how to solve it? Maybe the problem is that i use readyRead() signal?
Best regars.

Using readyRead() is OK, but you need to check, in your code, that all of the expected data is received, before processing it.
This is usually done by having start and stop characters in the messages and checking that received data contains both before processing(looks like you are already using "#" and "$ for this). And, since reading from QSerialPort will clear its buffer, you will also need to buffer received data in your code untill it is ready to be processed.
You could do something like this:
QByteArray rxBuffer;
void MainWindow::serialReceive()
{
rxBuffer.append(serialRx -> readAll());
while(rxBuffer.contains("#") && rxBuffer.contains("$")) {
QByteArray message;
// Discard any data preceding message start character
rxBuffer = rxBuffer.right(rxBuffer.length() - rxBuffer.indexOf("#"));
// Take first complete message from the buffer
message = rxBuffer.left(rxBuffer.indexOf("$") + 1);
// Remove extracted message from buffer
rxBuffer = rxBuffer.right(rxBuffer.length() - rxBuffer.indexOf("$") - 1);
// Your code here
}
}

I've ran into problems with QSerialPort recieving messages over multuple readyRead signals. Having an accumulation buffer may to the trick for you, but you will need either a start or end identifier.

Related

QSerialPort readings refresh too fast for the Qt widget

I am using QSerialPort to read from a device connected to a COM port on my computer, and it sends characters every half a second to my computer. I can read them from the qDebug window, so I know the connection works and Qt receives the data.
However I continuously read from the serial port and refresh a label widget on my GUI. The label becomes blank when I run the app, I think this problem is caused by the label name constantly refreshing.
My QserialPort is managed in the mainwindow constructor, closed in destructor, and the readings are done in a function called serialReceived(), which I believe is called (or causes the label to refresh) too often
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
serial = new QSerialPort(this);
qDebug() << "nb ports: " << QSerialPortInfo::availablePorts().length();
foreach(const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts())
{
qDebug() << "name" << serialPortInfo.portName();
}
serial->setPortName("COM11");
serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
qDebug() << "is " << serial->open(QSerialPort::ReadOnly);
qDebug() << "err " << serial->error();
//serial->write("ok");
// Create the signal and slot
connect(serial, SIGNAL(readyRead()), this, SLOT(serialReceived()));
}
MainWindow::~MainWindow()
{
delete ui;
serial->close(); // instance is closed when mainwindow destroyed
}
void MainWindow::serialReceived()
{
QByteArray ba;
ba = serial->readAll();
ui->label->setText(serial->readAll());
qDebug()<<ba;
}
void MainWindow::serialReceived()
{
QByteArray ba;
ba = serial->readAll();
ui->label->setText(serial->readAll());
qDebug()<<ba;
}
You're first reading the data into ba, then you try to read again but since readAll() already read the data there is nothing left. You want
void MainWindow::serialReceived()
{
QByteArray ba = serial->readAll();
ui->label->setText(ba);
qDebug() << ba;
}
You just can read data at any time you want, not only by readyRead signal. The QSerialPort class will buffer all received data until you read it.
You also can append every received part of data to some scrollable QPlainTextEdit. I recommend this way.
void MainWindow::serialReceived()
{
QByteArray ba;
ba = serial->readAll();
ui->plainTextEdit->appendPlainText(ba);
}
Using timer:
connect(&m_timer, &QTimer::timeout, this, &MyClass::onTimer);
...
m_timer->start(5000);
...
void MyClass::onTimer()
{
if(serial->bytesAvailable() > 0)
{
QByteArray ba;
ba = serial->readAll();
ui->label->setText(ba);
qDebug() << ba;
}
}
You can also temporary disable visual updates of a widget using QWidget::setUpdatesEnabled(), but seems you should not miss part of the data.
Be note, QIODevice (and QSerialPort as its sublass, too) class makes no guarantee that a certain amount of data will be available on the readyRead event. For example, if you wrote 10 bytes to the port at a time on the other end, in some cases you will receive the signal that will allow less data to be available at the monent, that is, before all the transmitted bytes arrives.

Connect signal of superclass of object

I am working with a QProcess and have connected QProcess's signal readyReadStandardOutput().
That process normally spits out data to the console regularly, but the readyReadStandardOutput() seems to batch results and only emit every half a minute or so (with all accumulated data).
I want to access the "live feed" of the QProcess so I thought maybe QProcess's superclass QIODevice has some other signals.
Other solutions instead of using bytesWritten are also welcome
Now I'm trying to connect bytesWritten, but it doesn't let me.
Code:
void MainWindow::on_Program_clicked() {
program= new QProcess(this);
QString file = "../folder/program/program.exe";
QString directory = "../folder/program/";
//qint64 pid;
program->setWorkingDirectory(directory);
program->start(file, {""});
program->waitForStarted();
connect(program, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput()));
//ERROR: "QObject::connect: No such signal QProcess::bytesWritten() in ..\---\mainwindow.cpp:45
connect(program, SIGNAL(bytesWritten()), this, SLOT(myBytesWritten()));
}
void MainWindow::myBytesWritten() {
QProcess *program = dynamic_cast<QProcess *>(sender());
QByteArray outData = program->readAll();
qDebug() << "DEBUG: " + outData;
}
//Works, but only emits a signal every so often, and not every time a new line is written to the console as when I launch the exe normally
void MainWindow::readOutput(){
QProcess *program = dynamic_cast<QProcess *>(sender());
QByteArray outData = program->readAll();
qDebug() << "DEBUG: " + outData;
}
If you want to use the old, deprecated way of connecting signals to slots, you need to also include the parameter list:
connect(program, SIGNAL(bytesWritten(int)), this, SLOT(myBytesWritten()));
Or you can just use the modern, compile-time Qt5 way:
connect(program, &QProcess::bytesWritten, this, &MainWindow::myBytesWritten);
Also note that bytesWritten fires when a write command from YOUR end has succeeded. You don't seem to be sending any input so this will never fire.

Qt Concurrent with signals & slots

I'm a novice in threads and someone advises me to use Qt Concurrent (Qt C++).
I'm trying to run a function in a thread by using Qt Concurrent, my functions runs well but my signal/slot is never emitted.
However for your information if I launch my function without using thread everything works fine.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QFutureWatcher<void> *watcher1 = new QFutureWatcher<void>();
connect(watcher1, SIGNAL(finished()), this, SLOT(getSizeFinished()));
QString string = "http://ipv4.download.thinkbroadband.com/50MB.zip";
QFuture<void> future1 = QtConcurrent::run(this, &MainWindow::getRemoteFileSize, string);
watcher1->setFuture(future1);
}
void MainWindow::getSizeFinished()
{
qDebug() << "--- FINISHED ---";
}
void MainWindow::getRemoteFileSize(const QString &url)
{
qDebug() << "Function - getRemoteFileSize";
QNetworkRequest req;
m_netmanager = new QNetworkAccessManager();
req.setUrl(QUrl(url));
m_reply = m_netmanager->get(req);
connect(m_reply, SIGNAL(metaDataChanged()), this, SLOT(remoteHTTPHeader()));
}
void MainWindow::remoteHTTPHeader()
{
qDebug() << "Function - remoteHTTPHeader";
remoteSize = m_reply->header(QNetworkRequest::ContentLengthHeader).toInt();
qDebug() << "Remote File Size: " << remoteSize;
m_reply->deleteLater();
m_netmanager->deleteLater();
qDebug() << "SIZE " << remoteSize;
}
You probably don't need to create a connection in this case, you could call MainWindow::remoteHTTPHeader() right after m_reply = m_netmanager->get(req);.
You might want to check if the reply is effectively finished like so:
if (m_reply->isFinished()) {
remoteHTTPHeader();
} else {
connect(m_reply, &QNetworkReply::finished, this, &MainWindow::remoteHTTPHeader);
}
This way you handle both fast and slow connections. Also notice how I created the connection using function pointers instead of SIGNAL and SLOT macro, this syntax is better since it checks at compile time if the functions exist so you avoid making typos and the like.

Can QSerialPort read more than 512 bytes of data?

I want to use QSerialPort to read data transmitted from a device. The device transmits a frame of 4000 data bytes each time. I try with the following simple code
QSerialPort *serialPort;
char receivedData[4000];
int numRead = 0;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* Initialize serial port*/
serialPort = new QSerialPort(this);
QString portName = "COM6";
qint32 baudRate = 460800;
serialPort->setPortName(portName);
serialPort->setBaudRate(baudRate);
serialPort->setDataBits(QSerialPort::Data8);
serialPort->setParity(QSerialPort::NoParity);
serialPort->setStopBits(QSerialPort::OneStop);
serialPort->setFlowControl(QSerialPort::NoFlowControl);
serialPort->setReadBufferSize(4000);
if (!serialPort->open(QIODevice::ReadOnly)) {
qDebug() << "Cannot open comport";
}
connect(serialPort, SIGNAL(readyRead()), this, SLOT(serialReceived()));
}
void MainWindow::serialReceived()
{
numRead = serialPort->read(receivedData, 4000);
serialPort->flush();
}
The problem is: it always shows that only 512 data bytes are read. How can I read the whole 4000 bytes data frame? (when I'm using Matlab to read this 4000 bytes frame, it's working fine)
There's no limit, but you don't necessarily receive all data in single chunk.
You have to keep listening until you have the number of bytes you're waiting for (or a timeout).
void MainWindow::serialReceived()
{
receivedData.append(serialPort->readAll());
if(receivedData.size() >= 4000) {
// we're full
}
}
You generally have to read out the data in a loop (to ensure you get it all), here is a snippet of example code this is equivalent to your serialReceived() function, except it emits the data using emit rxDataReady(newData); to whoever is listening...
void QSerialPortReader::handleReadyRead()
{
QByteArray newData;
// Get the data
while (mp_serialPort->bytesAvailable())
{
newData.append(mp_serialPort->readAll());
}
emit rxDataReady(newData);
}
edit
Although I don't do any max size checking... but that is trivial to add if you need it (i.e. just use read(..., spaceAvail) instead of readAll and then decrement spaceAvail...

QT serial port not working

I'm trying to write \ read from a serial device using a usb / rs 232 cable.
I'm pretty sure that my code writes " #002s " (this is a control code) to the serial device, because
a) the serial port is open
b) the serial port is writable
c) the code successfully navigates "wait For Bytes Written (-1)"
d) when using a serial port sniffer the data is successfully written.
The issue I have is that I don't receive any data, and no data is being emitted from the other device. When using qt terminal writing the same " #002s " produces the correct response.
Any ideas?
many thanks.
#include "test_serial.h"
#include "ui_test_serial.h"
#include <QtSerialPort/QtSerialPort>
#include <QDebug>
QSerialPort *serial;
Test_Serial::Test_Serial(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Test_Serial)
{
ui->setupUi(this);
serial = new QSerialPort(this);
connect(serial,SIGNAL(readyRead()),this,SLOT(serialReceived()));
serial->setPortName("COM1");
serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
serial->open(QIODevice::ReadWrite);
if (serial->isOpen() && serial->isWritable())
{
QByteArray input;
input = "#";
serial->write(input);
serial->waitForBytesWritten(-1);
input = "0";
serial->write(input);
serial->waitForBytesWritten(-1);
input = "0";
serial->write(input);
serial->waitForBytesWritten(-1);
input = "2";
serial->write(input);
serial->waitForBytesWritten(-1);
input = "s";
serial->write(input);
serial->waitForBytesWritten(-1);
input = "\r";
serial->write(input);
serial->waitForBytesWritten(-1);
serial->flush();
serial->waitForReadyRead(100);
QByteArray output = serial->readAll();
ui->label_2->setText(output);
}}
Test_Serial::~Test_Serial()
{
delete ui;
serial->close();
}
void Test_Serial::serialReceived()
{
QByteArray output;
output = serial->readAll();
ui->label->setText("output");
}
If you want to write " #002s ", Why not write at once? May be the serial device cant identify the control code when you write each character.
void Test_Serial::writeDataToSerialPort()
{
QByteArray input = QString("#002s").toLocal8Bit();
serial->write(input);
}
And no need for this reading part .
serial->waitForReadyRead(100);
QByteArray output = serial->readAll();
ui->label_2->setText(output);
The Test_Serial::serialReceived will be called any way when you have the response from the serial device.
And you can catch the error on opening the port by using the error signal from QSerialPort
connect(serial,SIGNAL(error(QSerialPort::SerialPortError)),this,SLOT(serialPortError(QSerialPort::SerialPortError)));
void Test_Serial::serialPortError(QSerialPort::SerialPortError error)
{
//Print error etc.
}
The issue ended up being that the readyread flag is only emitted if theirs data to read. However I was sending the data too quickly for the external device to receive it. This meant that some data was lost thus the device never recognised it as a valid command.
This meant that it was still waiting for the message to finish, hence the "IRP_MJ_DEVICE_CONTROL (IOCTL_SERIAL_WAIT_ON_MASK) UP STATUS_CANCELLED COM1" error message upon closing the program. This also explains why their were no error messages with regards to writing data.
This also explains why the same program occasionally managed to read data, and at other times failed, (even without rebuilding the program, just re-running it.) When the data was read, the processor was more loaded, i.e. programs running in the background. This meant that the data was transmitted more slowly and thus the external device could recognise the commands and thus reply.