QSerial error communicating with Arduino

2019-08-02 00:43发布

问题:

I wrote a simple program to use Qt (version 5.5 built from source for Visual Studio 2013 following this guide) to comunicate with Arduino.

Here is a simple firmware which read from the serial and after 10 seconds prints "1" back:

void setup() {
  Serial.begin(9600); //Open Serial connection for debugging
}

void loop() {
if (Serial.available()) {
    char user_input = Serial.read();
    if (user_input == '1') {
        delay(10000);
        Serial.print('1');
    }
}

Here is the Qt program which send "1" on serial when I press the start button and keeps listening for messages to read:

main.cpp

#include "mainwindow.h"
#include <QApplication>

#pragma comment(lib, "advapi32")

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.h

#pragma once

#include <QMainWindow>
#include "SerialHandler.h"

namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow* ui;
    SerialHandler serialHandler;
public slots:
    void onStart();
};

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent), ui(new Ui::MainWindow), serialHandler(QString("COM6")) {
    ui->setupUi(this);
    QObject::connect(ui->startButton, SIGNAL(released()), this, SLOT(onStart()));
}

MainWindow::~MainWindow() {
    delete ui;
}

void MainWindow::onStart() {
    serialHandler.moveForward();
}

SerialHandler.h

#pragma once

#include <QString>
#include <QtSerialPort>
#include <QtSerialPort/QSerialPort>


class SerialHandler {
public:
    SerialHandler(QString& serialName);
    ~SerialHandler();
    void moveForward();

private:
    QSerialPort serial;
    void writeOnSerial(QByteArray& msg);

private slots:
    void handleReadyRead();
};

SerialHandler.cpp

#include "SerialHandler.h"
#include <QDebug>
#include <QObject>


SerialHandler::SerialHandler(QString& serialName){
    connect(&serial, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));

    // Initialize Serial
    serial.setPortName(serialName);

    if (!serial.setBaudRate(QSerialPort::Baud9600)) {
        throw std::runtime_error(QObject::tr("Can't set rate 9600 baud to port %1, error code %2")
            .arg(serial.portName()).arg(serial.error()).toStdString());
    }

    if (!serial.open(QIODevice::ReadWrite)) {
        throw std::runtime_error(QObject::tr("Can't open %1, error code %2")
            .arg(serial.portName()).arg(serial.error()).toStdString());
    }

    if (!serial.setDataBits(QSerialPort::Data8)) {
        throw std::runtime_error(QObject::tr("Can't set 8 data bits to port %1, error code %2")
            .arg(serial.portName()).arg(serial.error()).toStdString());
    }

    if (!serial.setParity(QSerialPort::NoParity)) {
        throw std::runtime_error(QObject::tr("Can't set no patity to port %1, error code %2")
            .arg(serial.portName()).arg(serial.error()).toStdString());
    }

    if (!serial.setStopBits(QSerialPort::OneStop)) {
        throw std::runtime_error(QObject::tr("Can't set 1 stop bit to port %1, error code %2")
            .arg(serial.portName()).arg(serial.error()).toStdString());
    }

    if (!serial.setFlowControl(QSerialPort::NoFlowControl)) {
        throw std::runtime_error(QObject::tr("Can't set no flow control to port %1, error code %2")
            .arg(serial.portName()).arg(serial.error()).toStdString());
    }
}

SerialHandler::~SerialHandler(){
    if (serial.isOpen()) {
        serial.close();
    }
}

void SerialHandler::handleReadyRead() {
    qDebug() << serial.readAll();
}

void SerialHandler::writeOnSerial(QByteArray& msg) {
    if (serial.isOpen() && serial.isWritable()) {
        serial.write(msg);
        serial.flush();
    }
}

void SerialHandler::moveForward(){
    QByteArray msg("1");
    writeOnSerial(msg);
}

the problem appears in writeOnSerial method (SerialHandler.cpp). The call to serial.write() fires this error:

QObject::startTimer: Timers can only be used with threads started with QThread

the "1" is written on serial and read by the arduino, but the computer never receives anything back after 10 seconds, the readyRead() signal is never fired!

I saw the same error in this post, but in my case the exec loop is there!

Any suggestions?

回答1:

This minimal test case works for me on an Arduino on both Windows and OS X, using latest Qt 5.5.

This kind of minimization is what belongs in your question. Multiple files (and a missing .ui) aren't necessary to demonstrate such simple failures.

// https://github.com/KubaO/stackoverflown/tree/master/questions/miniserial-36431493
#include <QtWidgets>
#include <QtSerialPort>

int main(int argc, char ** argv) {
   QApplication app(argc, argv);
   QWidget w;
   QFormLayout layout(&w);
   QPushButton ping("Send");
   QTextBrowser output;
   layout.addRow(&ping);
   layout.addRow(&output);
   w.show();

   QSerialPort port("COM6");
   port.setBaudRate(QSerialPort::Baud9600); // these are guaranteed to return true
   port.setDataBits(QSerialPort::Data8);
   port.setParity(QSerialPort::NoParity);
   port.setStopBits(QSerialPort::OneStop);
   port.setFlowControl(QSerialPort::NoFlowControl);
   if (!port.open(QIODevice::ReadWrite))
      output.append("! Can't open the port :(<br/>");

   QObject::connect(&ping, &QPushButton::clicked, &port, [&]{
      if (port.isOpen()) {
         port.write("1");
         output.append("> 1<br/>");
      } else
         output.append("! Write failed :(<br/>");
   });
   QObject::connect(&port, &QIODevice::readyRead, &output, [&]{
      auto data = port.readAll();
      output.append(QStringLiteral("< %1<br/>")
                    .arg(QString::fromLatin1(data).toHtmlEscaped()));
   });

   return app.exec();
}