Double way Serial communication between Arduino and Qt 5.7

arduino, c++, qt, serial-port

Solution

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();
}

Problem

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,

Original source