Can QSerialPort read more than 512 bytes of data? - c++

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...

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.

Qt QSerialPort Read Write

I have issue with reading and writing from/to QSerialPort. I read what I have written. Here is some example code that proves that. Maybe I need to write and read from different channels. Code's provided below thx in advance. Provided log of the program with image.
Log of the program here
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QTimer>
#include <QDebug>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QSerialPort *serialPort;
QTimer* sendTimer;
QByteArray receiveDataBuffer;
private slots:
void onReadyRead();
void onSendDataTimeout();
};
#endif // MAINWINDOW_H
MainWindows.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
serialPort(new QSerialPort),
sendTimer(new QTimer)
{
sendTimer->setInterval(1000);
serialPort->setPortName("COM5");
serialPort->setBaudRate(QSerialPort::Baud115200);
serialPort->setParity(QSerialPort::Parity::NoParity);
serialPort->setDataBits(QSerialPort::DataBits::Data8);
serialPort->setStopBits(QSerialPort::StopBits::TwoStop);
serialPort->open(QSerialPort::OpenModeFlag::ReadWrite);
connect(serialPort,&QSerialPort::readyRead,this,&MainWindow::onReadyRead);
connect(sendTimer,&QTimer::timeout,this,&MainWindow::onSendDataTimeout);
sendTimer->start();
}
MainWindow::~MainWindow()
{
}
void MainWindow::onReadyRead()
{
receiveDataBuffer.append(serialPort->readAll());
if (receiveDataBuffer.contains('^') && receiveDataBuffer.contains('$') && receiveDataBuffer.lastIndexOf('$') < receiveDataBuffer.lastIndexOf('^')) {
QByteArray extractedByteArray;
for(int i = receiveDataBuffer.lastIndexOf("$") ; i < receiveDataBuffer.lastIndexOf("^") + 1 ; i++){
extractedByteArray.append(receiveDataBuffer[i]);
}
qDebug()<<"Received:"<<extractedByteArray<<endl;
receiveDataBuffer.clear();
}}
void MainWindow::onSendDataTimeout()
{
qint64 result = serialPort->write("$10,0,123,123^");
bool flush = serialPort->flush();
result != -1 && flush ? qDebug()<<"Data sent"<<endl:qDebug()<<"Failed to send data"<<endl;
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
First of all, try to connect the different signals that are emitted when you read/write that from your port.
For instance to check that the data has been sent properly, according to the documentation you can use the signal QIODevice::bytesWritten:
The signal is emitted every time a payload of data has been written to the device's current write channel. The bytes argument is set to the number of bytes that were written in this payload.
connect(serialPort,&QSerialPort::bytesWritte, this, [](const qint64 bytes) {
qDebug() << "Sent data: " << bytes << " bytes.";
});
To read the data, as you already did, you need to check the QIODevice::readyRead signal:
This signal is emitted once every time new data is available for reading from the device's current read channel. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.
Regarding the problem you described, the first step to do is to ensure that your decive has not been configured to work in echo-mode. To turn it off, you will need to check your device interface.
If this is not the problem, check the available ports by using `QSerialPort::availablePorts and ensure that you are connecting to the right one. For example, to connect to the first port, you can do something like this and check that the configuration is compatible with your device:
auto serialInfo = QSerialPortInfo::availablePorts();
serialPort->setPort(serialInfo[0])
auto ret = serialPort.open(QSerialPort::ReadWrite)
&& serialPort.setBaudRate(QSerialPort::Baud38400)
&& serialPort.setDataBits(QSerialPort::Data8)
&& serialPort.setStopBits(QSerialPort::OneStop)
&& serialPort.setParity(QSerialPort::NoParity);
qDebug() << "Port has been configured properly?: " << ret;
If the problem persist, you can get the native handler and modify its properties by calling QSerailPort::handle().
If the platform is supported and the serial port is open, returns the native serial port handle; otherwise returns -1.
The last option, it does not work in all platforms. Worse scenario, take a look into the official docs of termios. It provides a low-level interface that allows you to enable/disable the echo mode.
struct termios options;
tcgetattr(file, &options);
cfmakeraw(&options);
options.c_lflag &= ~(ECHO | ECHOE); // Add or disable the flags
tcsetattr(file, TCSANOW, &options);
The problem was in
serialPort->setStopBits(QSerialPort::StopBits::TwoStop);
The other device was connected to serial port with QSerialPort::StopBits::OneStop.
So changing the stop bits serialPort->setStopBits(QSerialPort::StopBits::OneStop);
solved the problem and there were no echoes anymore.Thx.

Sending images over TCP from labVIEW to QT

I am trying to capture images taken from a camera connected to a myRIO and send them over a TCP/IP connection from labVIEW to a QT GUI application.
My problem is that QT keeps throwing a heap pointer exception and crashing when I read the data.
Expression: is_block_type_valid(header->_block_use)
I believe this could be because the data being sent is over 35k bytes, so I tried to read the data in separate chunks, but alas am still getting the error.
Below is my function that gets called on readyRead() being emitted:
void TCPHandler::onRead() {
QByteArray byteArray;
QByteArray buffer;
QByteArray dataSize = mainSocket->read(5); //read the expected amount of bytes incoming (about 35000)
while (buffer.size() < dataSize.toInt()) {
int bytesLeft = dataSize.toInt() - buffer.size();
if (bytesLeft < 1024) {
byteArray = mainSocket->read(bytesLeft);
}
else {
byteArray = mainSocket->read(1024);
}
buffer.append(byteArray);
}
QBuffer imageBuffer(&buffer);
imageBuffer.open(QIODevice::ReadOnly);
QImageReader reader(&imageBuffer, "JPEG");
QImage image;
if(reader.canRead())
image = reader.read();
else {
emit read("Cannot read image data");
}
if (!image.isNull())
{
image.save("C:/temp");
}
else
{
emit read(reader.errorString());
}}
In the LabVIEW code I send the size of the bytes being sent first, then the raw image data:
EDIT: Connect for the slot. Also should have mentioned this is running in a separate thread to the Main GUI.
TCPHandler::TCPHandler(QObject *parent)
: QObject(parent),
bytesExpected(0)
{
mainSocket = new QTcpSocket(this);
connect(mainSocket, SIGNAL(readyRead()), this, SLOT(onRead()));
connect(mainSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &TCPHandler::displayError);
}
You are sending your length as a decimal string. Then followed by the string.
I would expect that the length would be binary value. So instead of an 'I32 to String' function use a typecast with a string as the type.

Communication error between Arduino and Qt using Xbee PRO S1

I've been trying to do a Lights GUI with an Arduino Mega 2560 with its Xbee Shield and two Xbee Pro S1, one connected to the Arduino and the other one to the PC. My problem is: however I can send data from Qt to my arduino and read it, i can't do the same in the other way. When trying to send a String as "Confirmado\r\n", it arrives to my Qt label wrong, sometimes I get the full String, other ones I receive half of it.
My arduino code is
char buffer[50];
String trama, dir, com, data;
int indexdir, indexcom, indexdata;
void setup(){
Serial.begin(9600);
}
void loop(){
trama= "Confirmado\r\n";
const char *bf = trama.c_str();
if(Serial.available() > 0)
{
Serial.readBytesUntil('/', buffer,500);
Serial.print(bf);
Serial.flush();
}
}
My Qt QSerialPort config is
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
serial = new QSerialPort(this);
serial->setPortName("COM3"); //COM-port your Arduino is connected to
serial->open(QIODevice::ReadWrite);
serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
connect(serial,SIGNAL(readyRead()),this,SLOT(serialReceived()));
}
And I send and read data like this
void MainWindow::serialReceived()
{
QByteArray readData = serial->readAll();
//while (serial->waitForReadyRead(500))
// readData.append(serial->readAll());
ui->label->setText(readData);
}
void MainWindow::writeData(const QByteArray &data)
{
serial->write(data);
serial->flush();
serial->waitForBytesWritten(500);
}
The toogle lines means I've tried both options...
I've noticed, doing Debug, that if I place a breakpoint in ui->label->setText(readData);; if it doesnt arrive well (the full "Confirmado\r\n" string), this breakpoint gets twice in this line, the first one readData equals the second half of the string (i.e "mado\r\n") and the other one it values the rest of the string (i.e "Confir").
I've also tried to set a higher baudrate, 57600, but I cant send or receive any data, though I've set the baudrate in the XCTU app before.
Does anyone know a way to receive the full string from Arduino? Or at leats how to setup properly Arduino's and PC's Xbee to work with higher baudrates?
Thanks for the answers, and sorry for my writing skills...
Try use serial->readLine() instead of serial->readall() you can for example wait in loop after the serial->canReadLine() returns the true then you be sure that the data are you received is a full string.

UART to Qt software error - why data always split?

I am tryig to display data I receive via UART on my Qt application.
I send all my data from my microcontroller at once, but my qt application receives it in multiple parts why?
this is what I get: http://imgur.com/kLXRvU5
in stead of: http://imgur.com/h2yNZjl
So every time I receive data my slot function gets called, hence the "data received". But my data is split in two parts. Why please?
my code:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)//, mijnAnimatie(new animatie())
{
serialPort->setPortName("COM13");
serialPort->setBaudRate(QSerialPort::Baud115200);
// serialPort->setDataBits(QSerialPort::Data8);
// serialPort->setParity(QSerialPort::NoParity);
// serialPort->setStopBits(QSerialPort::OneStop);
if (!serialPort->open(QIODevice::ReadWrite))
{
qDebug("some error when opening\n");
}
connect(serialPort, SIGNAL(readyRead()), this, SLOT(updateReceivedData()));
}
void MainWindow::updateReceivedData()
{
qDebug("received data\n");
QString m_readData;
ui->receiveLabel->setText(m_readData);
QByteArray result = serialPort->readAll();
QString command(result); //to convert byte array to string
qDebug()<<result;
ui->receiveLabel->setText(command);
}
Streaming connections, like TCP/IP or serial ports, give you zero guaranties about how the data is cut up into pieces. If you want packets, you have to implement them yourself on top of a streaming connection.