I am trying to transfer data from Arduino to a C++ Qt5.7 and from a Arduino to a C++ Qt5.7 (MinGW) program.
I am able to transfer the data FROM QT TO ARDUINO without any problems.
The Arduino blinks perfectly.
On the other hand, the data transfered FROM THE ARDUINO TO QT isnt always the expected (sends "LED ON" when it should be "LED OFF") and sometimes it doesnt communicate at all!
QT code:
#include <QCoreApplication>
#include <QDebug>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QThread>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSerialPort serial;
serial.setPortName("COM6");
serial.setBaudRate(9600);
serial.setDataBits(QSerialPort::Data8);
serial.setParity(QSerialPort::NoParity);
serial.setStopBits(QSerialPort::OneStop);
serial.setFlowControl(QSerialPort::NoFlowControl);
if(serial.open(QSerialPort::ReadWrite))
{
string c;
QByteArray s;
QByteArray received;
while(true)
{
qDebug("TRUE");
//WRITE
cin >> c;
cout << endl;
s = QByteArray::fromStdString(c);
serial.write(s);
serial.waitForBytesWritten(-1);
//serial.flush();
s = serial.readAll();
serial.waitForReadyRead(-1);
cout << s.toStdString() << endl;
//serial.flush();
}
}
else
{
QString error = serial.errorString();
cout << error.toStdString() << endl;
qDebug("FALSE") ;
}
serial.close();
return a.exec();
}
The ARDUINO CODE:
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
}
// the loop function runs over and over again forever
void loop() {
delay(1000); // wait for a second
}
void serialEvent()
{
char inChar;
while (Serial.available())
{
// get the new byte:
inChar = (char)Serial.read();
if(inChar == 'a')
{
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
}
else
{
digitalWrite(LED_BUILTIN, LOW); // turn the LED off (LOW is the voltage level)
}
}
delay(500);
if(inChar == 'a')
{
Serial.write("LED ON");
}
else
{
Serial.write("LED OFF");
}
}
Terminal Image of Error:
TERMINAL WITH ERROR image
Please help!
Thank you,
You have no packetization whatsoever: there are no delimiters between individual chunks of data - other than time passing.
On the Arudino side, instead of write
, you should use println
so that each message is a complete line.
On the Qt side, process complete lines. You're not guaranteed to get a complete response from the serial port after waitForReadyRead
. All that you're guaranteed is that at least 1 byte is available to read. That is the source of your problem. Note how you got LE
, then sometime later you got D OFF
immediately followed by LED ON
. You must wait for data until complete line(s) are available.
The following should work on the Qt end of things - also note that you don't need as many includes, and you can use QTextStream
instead of iostream, to lower the number of APIs that you use. Finally, you don't need app.exec
since you write blocking code.
// https://github.com/KubaO/stackoverflown/tree/master/questions/arduino-read-40246601
#include <QtSerialPort>
#include <cstdio>
int main(int argc, char *argv[])
{
QCoreApplication a{argc, argv};
QTextStream in{stdin};
QTextStream out{stdout};
QSerialPort port;
port.setPortName("COM6");
port.setBaudRate(9600);
port.setDataBits(QSerialPort::Data8);
port.setParity(QSerialPort::NoParity);
port.setStopBits(QSerialPort::OneStop);
port.setFlowControl(QSerialPort::NoFlowControl);
if (!port.open(QSerialPort::ReadWrite)) {
out << "Error opening serial port: " << port.errorString() << endl;
return 1;
}
while(true)
{
out << "> ";
auto cmd = in.readLine().toLatin1();
if (cmd.length() < 1)
continue;
port.write(cmd);
while (!port.canReadLine())
port.waitForReadyRead(-1);
while (port.canReadLine())
out << "< " << port.readLine(); // lines are already terminated
}
}
If you wish, you can also easily turn it into a GUI application, it's only a few lines to do so:
#include <QtSerialPort>
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication app{argc, argv};
QWidget ui;
QFormLayout layout{&ui};
QLineEdit portName{"COM6"};
QTextBrowser term;
QLineEdit command;
QPushButton open{"Open"};
layout.addRow("Port", &portName);
layout.addRow(&term);
layout.addRow("Command:", &command);
layout.addRow(&open);
ui.show();
QSerialPort port;
port.setBaudRate(9600);
port.setDataBits(QSerialPort::Data8);
port.setParity(QSerialPort::NoParity);
port.setStopBits(QSerialPort::OneStop);
port.setFlowControl(QSerialPort::NoFlowControl);
QObject::connect(&open, &QPushButton::clicked, &port, [&]{
port.setPortName(portName.text());
if (port.open(QSerialPort::ReadWrite)) return;
term.append(QStringLiteral("* Error opening serial port: %1").arg(port.errorString()));
});
QObject::connect(&command, &QLineEdit::returnPressed, &port, [&]{
term.append(QStringLiteral("> %1").arg(command.text()));
port.write(command.text().toLatin1());
});
QObject::connect(&port, &QIODevice::readyRead, &term, [&]{
if (!port.canReadLine()) return;
while (port.canReadLine())
term.append(QStringLiteral("< %1").arg(QString::fromLatin1(port.readLine())));
});
return app.exec();
}
I think you must use EOL and Carrige return character on QT. Try to replace Serial.write
to Serial.println
in Arduino code.