Why the intruction ReadAll() block the serial port reading?

95 Views Asked by At

I'm trying to read double sent by arduino on the serial port with an qt program. But i have a problem: i connected the signal readyRead to a slot named newData, in this slot when i execute the intruction readAll() everything run correctly, but when i comment it it seems that the signal readyRead is never reemitted again and we never come back in the slot. I realized it by printing a string on the terminal when the slot is called.

First case : readAll() executed in the slot:

qt.core.qmetaobject.connectslotsbyname: QMetaObject::connectSlotsByName: No matching signal for on_on_clicked()
qt.core.qmetaobject.connectslotsbyname: QMetaObject::connectSlotsByName: No matching signal for on_off_clicked()
conecion ok
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donnée
bonjour je recoit une donn

and so on ....

second case: readAll() is commented:

13:07:59: Démarrage de C:\Users\user\Documents\GUI2\debug\monappli.exe...
qt.core.qmetaobject.connectslotsbyname: QMetaObject::connectSlotsByName: No matching signal for on_on_clicked()
qt.core.qmetaobject.connectslotsbyname: QMetaObject::connectSlotsByName: No matching signal for on_off_clicked()
conecion ok
bonjour je recoit une donnée

here is my seriallink class which has a member named "_serial" that is a QSerialPort variable:

seriallink.h:

#ifndef SERIALLINK_H
#define SERIALLINK_H
#pragma once
#include <QObject>
#include <QSerialPort>
class seriallink : public QObject
{
    Q_OBJECT
public:
    explicit seriallink(QObject *parent = nullptr);
    ~seriallink(); //destructor


    void openConnection();
    void closeConnection();
    void write (const char* messageToWrite);
    bool isOpen();
    bool isWritable();
    void data_process(QByteArray read);

    // a slot is a method that can be used in the class derivated ob QObject and that are called
    // when a signal is emitted by a Q object.
private slots:
    void newData();


signals:
    void gotNewData(QByteArray data);
    void availableData(QByteArray data);
private:
    QByteArray _data;
    QSerialPort _serial;

};

#endif // SERIALLINK_H

seriallink.cpp:

#include "seriallink.h"
#include <QDebug>
#include <iostream>

seriallink::seriallink(QObject *parent)
    : QObject{parent}
{
    _serial.setPortName("COM3");
    _serial.setBaudRate(QSerialPort::Baud9600);
    _serial.setDataBits(QSerialPort::Data8);
    _serial.setParity(QSerialPort::NoParity);
    _serial.setStopBits(QSerialPort::OneStop);
    _serial.setFlowControl(QSerialPort::NoFlowControl);
    _serial.setReadBufferSize(QSerialPort::Data8);
    //the method connect enable to link a slot and a signal.
    // _serial is the emettor
    // _QSerialPort::readyRead is the name of the signal that we wants to intercept
    // this is the object that contain the slot that is going to react to the generation of the signal
    //  &seriallink::newData is the slot
    connect(& _serial, &QSerialPort::readyRead, this, &seriallink::newData);
    //connect(this,  &seriallink::gotNewData, this, &seriallink::data_process);

}

seriallink::~seriallink(){
    closeConnection();
}

void seriallink::openConnection(){
    if(!_serial.open(QIODevice::ReadWrite))
        qDebug()<<"impossible connection";

    else qDebug() <<"conecion ok";
}


void seriallink::closeConnection(){
    _serial.close();
}

void seriallink::write(const char* messageToWrite){
    _serial.write(messageToWrite);
}

void seriallink::newData(){
        Qdebug()<<"bonjour je reçoit une donnée"; 
        _serial.readAll();

       
}



bool seriallink::isOpen(){
    return _serial.isOpen();
}

bool seriallink::isWritable() {
    return _serial.isWritable();
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ihmGraphique = new IhmGraphique();
    //ui->graph_frame->layout()->addWidget(ihmGraphique->get_graphique());
    //setCentralWidget(ihmGraphique->graphique);
    ui->scrollArea->setWidget(ihmGraphique->get_graphique());
    arduino= new seriallink;
    arduino->openConnection();
    //connect(arduino, &seriallink::gotNewData, this , &MainWindow::updateGUI);
    //on crée aussi un fichier
    file=new datafile(nullptr, "DATA.txt");
}



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

void MainWindow::updateGUI(QByteArray data){

//    ui->byte_received->display(ui->byte_received->value()+data.size());
}

void MainWindow::on_on_clicked()
{
    if (arduino->isWritable())
        arduino->write("o");
    else
        qDebug() <<"could not write to serial";
}


void MainWindow::on_off_clicked()
{
    if (arduino->isWritable())
        arduino->write("o");
    else
        qDebug() <<"could not write to serial";
}




void MainWindow::on_StartSaving_clicked()
{
    //connect(arduino, &seriallink::gotNewData, this , &MainWindow::updateGUI);
    std::cout<<"je suis ici"<<std::endl;
    connect(arduino, &seriallink::availableData, file , &datafile::writeData);
}


void MainWindow::on_StopSaving_clicked()
{
    disconnect(arduino, &seriallink::availableData, file , &datafile::writeData);
    file->close_file();

}



void MainWindow::on_StartPloting_clicked()
{   //QDateTime debut = QDateTime::currentDateTime();
    // je vais juste essayer ici de changer quelque chose qui peut se voir sur le graphe

    QDateTime current_time = QDateTime::currentDateTime();
    //ici on doit set le debut du graph
    auto xAxis = ihmGraphique->graphe->axes(Qt::Horizontal).front();
    xAxis->setMin(current_time.addSecs(10));
    connect(arduino, &seriallink::gotNewData, ihmGraphique , &IhmGraphique::addData);
}

the data send by the arduino look like that:

2.10,8.28,6.34,7.49,5.99,1.09,9.14,8.31,9.91,9.81,5.27,2

i tried the code of a guy on youtube that is doing the same thing of me, and the string is printed without executing th readAll() function. Here is his code :

#include "dialog.h"
#include "ui_dialog.h"
#include <QSerialPort>
#include <QSerialPortInfo>
#include <string>
#include <QDebug>
#include <QMessageBox>
#include <iostream>


Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    ui->temp_lcdNumber->display("-------");
    arduino = new QSerialPort(this);
    serialBuffer = "";
    parsed_data = "";
    temperature_value = 0.0;

    /*
     *  Testing code, prints the description, vendor id, and product id of all ports.
     *  Used it to determine the values for the arduino uno.
     *
     *
    qDebug() << "Number of ports: " << QSerialPortInfo::availablePorts().length() << "\n";
    foreach(const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()){
        qDebug() << "Description: " << serialPortInfo.description() << "\n";
        qDebug() << "Has vendor id?: " << serialPortInfo.hasVendorIdentifier() << "\n";
        qDebug() << "Vendor ID: " << serialPortInfo.vendorIdentifier() << "\n";
        qDebug() << "Has product id?: " << serialPortInfo.hasProductIdentifier() << "\n";
        qDebug() << "Product ID: " << serialPortInfo.productIdentifier() << "\n";
    }
    */


    /*
     *   Identify the port the arduino uno is on.
     */
    bool arduino_is_available = false;
    QString arduino_uno_port_name;
    //
    //  For each available serial port
    foreach(const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()){
        //  check if the serialport has both a product identifier and a vendor identifier
        if(serialPortInfo.hasProductIdentifier() && serialPortInfo.hasVendorIdentifier()){
            //  check if the product ID and the vendor ID match those of the arduino uno
            if((serialPortInfo.productIdentifier() == arduino_uno_product_id)
                    && (serialPortInfo.vendorIdentifier() == arduino_uno_vendor_id)){
                arduino_is_available = true; //    arduino uno is available on this port
                arduino_uno_port_name = serialPortInfo.portName();
            }
        }
    }

    /*
     *  Open and configure the arduino port if available
     */
    arduino_is_available = true;
    if(arduino_is_available){
        qDebug() << "Found the arduino port...\n";
        arduino->setPortName("COM3");
        arduino->open(QSerialPort::ReadOnly);
        arduino->setBaudRate(QSerialPort::Baud9600);
        arduino->setDataBits(QSerialPort::Data8);
        arduino->setFlowControl(QSerialPort::NoFlowControl);
        arduino->setParity(QSerialPort::NoParity);
        arduino->setStopBits(QSerialPort::OneStop);
        QObject::connect(arduino, SIGNAL(readyRead()), this, SLOT(readSerial()));
    }else{
        qDebug() << "Couldn't find the correct port for the arduino.\n";
        QMessageBox::information(this, "Serial Port Error", "Couldn't open serial port to arduino.");
    }
}

Dialog::~Dialog()
{
    if(arduino->isOpen()){
        arduino->close(); //    Close the serial port if it's open.
    }
    delete ui;
}

void Dialog::readSerial()
{
    /*
     * readyRead() doesn't guarantee that the entire message will be received all at once.
     * The message can arrive split into parts.  Need to buffer the serial data and then parse for the temperature value.
     *
     */
    QStringList buffer_split = serialBuffer.split(","); //  split the serialBuffer string, parsing with ',' as the separator

    //  Check to see if there less than 3 tokens in buffer_split.
    //  If there are at least 3 then this means there were 2 commas,
    //  means there is a parsed temperature value as the second token (between 2 commas)
    qDebug() << serialBuffer << "\n";
    if(false){
    if(buffer_split.length() < 3){
        // no parsed value yet so continue accumulating bytes from serial in the buffer.
        serialData = arduino->readAll();
        serialBuffer = serialBuffer + QString::fromStdString(serialData.toStdString());
        serialData.clear();
    }else{
        // the second element of buffer_split is parsed correctly, update the temperature value on temp_lcdNumber
        serialBuffer = "";
        qDebug() << buffer_split << "\n";
        parsed_data = buffer_split[1];
        temperature_value = (9/5.0) * (parsed_data.toDouble()) + 32; // convert to fahrenheit
        qDebug() << "Temperature: " << temperature_value << "\n";
        parsed_data = QString::number(temperature_value, 'g', 4); // format precision of temperature_value to 4 digits or fewer
        Dialog::updateTemperature(parsed_data);
    }
    }

}

void Dialog::updateTemperature(QString sensor_reading)
{
    //  update the value displayed on the lcdNumber
    ui->temp_lcdNumber->display(sensor_reading);
}

1

There are 1 best solutions below

0
hyde On

The signal readyRead() will not be emitted again until you have read some data.

Solution: add byte array buffer as member variable:

void seriallink::newData(){
    _byteArrayBuffer.append(_serial.readAll());
    tryParseBuffer();
}

Then write tryParseBuffer(), which removes complete and parseable data from the beginning of the buffer, but leaves incomplete data there, for the next try.