Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Write ASCII Character and read response using serialport



  • capture.png
    Hi. I'm making GUI which displays encoder's position on the QTextBrowser. I communicate with my device through serialport, So I use QSerialport.
    The picture I attach is the some parts of the document.

    class MotorPort : public QSerialPort
    /// this is my MotorPort class.
    void MotorPort::openMotorPort(const QString &portName)
    {
        setPortName(portName);
        setBaudRate(defaultBaudRate);
        setDataBits(defaultDataBits);
        setStopBits(defaultStopBits);
        setParity(defaultParity);
        open(QIODevice::ReadWrite);
    
    }
    

    based on this I wrote code like below.

    /// In MainWindow.h
    MotorPort *rls;
    /// In MainWindow.cpp
    rls->openMotorPort("COM5"); //I Checked my PC and device are connected!
    
    rls->write(//something//);
    QByteArray data = rls-> readAll();
    
    ui->statusencoder->setText(QString(data)); //statusencoder is QTextBrowser
    
    

    The problem is I don't know what to put in the rls->write(). I try to make code like this one, but It doesn't work.

    char ch1(63); //63 means "?" in ASCII table.
    rls->write(&ch1);
    

    Can you let me know how to send ASCII character? Also, Is QByteArray data = rls-> readAll(); enough to read the response?

    Thank you


  • Lifetime Qt Champion

    Hi

    You can use
    QByteArray buffer = "?";
    serial->write(buffer);

    • rls-> readAll(); enough to read the response?

    No, not always. You should read until you see CR ( as shown in your table)

    Also you cant just call it. you normally use the signal QSerialPort::readyRead and then call readall()
    More than one signal might come.

    So not sure you are using it correctly.


  • Lifetime Qt Champion

    Hi,

    In addition to @mrjj, you should always check that the QSerialPort open method returned successfully. You may be using the correct port name but not have access to the device for different reasons.



  • Sorry for the late reply.
    After reading @mrjj 's answer, I added readyRead.

    // Constructor
    MotorPort::MotorPort(QSerialPort* parent) : QSerialPort(parent)
    {
        // Connect signals to slots.
        connect(this, SIGNAL(readyRead()), this, SLOT(getData()));
    
        // Default port connection parameters.
    
        defaultBaudRate = QSerialPort::Baud9600;
        defaultDataBits = QSerialPort::Data8;
        defaultStopBits = QSerialPort::OneStop;
        defaultParity   = QSerialPort::NoParity;
    
    }
    
    // Destructor
    MotorPort::~MotorPort()
    {
    
    }
    
    bool MotorPort::openMotorPort(const QString &portName)
    {
        setPortName(portName);
        setBaudRate(defaultBaudRate);
        setDataBits(defaultDataBits);
        setStopBits(defaultStopBits);
        setParity(defaultParity);
        bool openning;
        opening = open(QIODevice::ReadWrite);
        return openning;
    }
    
    void MotorPort::getData()
    {
        Output = QIODevice::readAll(); // Output(QByteArray) is private member of MotorPort
    }
    
    QByteArray MotorPort::getresponse()
    {
        QByteArray buffer = "?";
        write(buffer);
        getData();
        return Output;
    }
    

    and in mainwindow.cpp

    bool open = rls->openMotorPort("COM5");
    if (open==true)
    {
        ui->statusencoder->append("success");
        QByteArray data=rls->getresponse();
        ui->statusencoder->append(QString(data));
        int array[10]; //I tried to choose return value type as int
        for (int i=0;i<10;i++)
         {
                array[i]=7;
                ui->statusencoder->append(QString::number(array[i]));
                array[i]=data[i];
                ui->statusencoder->append(QString::number(array[i]));
          }
    
    }
    

    After running this code, My QTextBrowser(statusencoder) shows like this one

    success
    
    7
    0
    7
    0
    7
    0
    7
    0
    7
    0
    7
    0
    7
    0
    7
    0
    7
    0
    7
    0
    

    the reference software says encoder's position is 3.032mm. so I expect any different value. but QByteArray data seems to be NULL. Am I still making code wrong?


  • Lifetime Qt Champion

    Hi
    Its asynchronous so i think the issue lies here

    QByteArray MotorPort::getresponse()
    {
        QByteArray buffer = "?";
        write(buffer); // send 
        getData();// you just call it so might be nothing to read yet.  its not called by serialport due to incoming data
        return Output;
    }
    

    So you cant really do it this way. it has to be like this

    You send something
    Serialport issues one or more readyRead signals
    in getData you read and append to a buffer

    Output += QIODevice::readAll(); // note the plus

    then when data contains a CR, we know you got full set.
    Then you can send signal to mainWindow that data is ready.
    and it that slot in main window, you can then use the data.

    I know it seems a bit complicated but thats how async programming is.

    If you really find it hard to get work, you can use the blocking api of QSrialport
    https://doc.qt.io/qt-5/qtserialport-creadersync-example.html

    But do note that this LAGS the GUI. gui will not redraw etc while code is running and that is
    why the async way with signal and slot is preferred.



  • @mrjj
    Hi. I read your answer but It is a little difficult for me to understand the concept of asynchronous, I googled it and some functions, I changed MotorPort::getresponse() like below. then it works

    QByteArray MotorPort::getresponse()
    {
        QByteArray buffer = "?";
        write(buffer);
        waitForReadyRead(500);
        return Output;
    }
    

    Is this the solution of your answer? Actually I don't think I fully understood your answer.
    Anyway, Thank you mrjj!


  • Lifetime Qt Champion

    @H-dragon
    Hi
    asynchronous means it will happen at another point in time (than right now) and yes it can be
    difficult to handle.

    You used the blocking call
    waitForReadyRead(500);
    so yes that was what i meant in the second part of the answer.
    Even now you kinda mix sync and async but it seems that it fires in right order
    so it works :)



  • @mrjj
    HI.
    Can I ask you one last question?

    QByteArray MotorPort::getresponse()
    {
        QByteArray buffer = "?";
        write(buffer);
        waitForReadyRead(500);
        return Output;
    }
    

    How the private member Output gets some data from serialport when I call the function getresponse(), although there isnt the function getData()? As I know, the waitForReadyRead(500) blocks the cell until new data is available.
    Is that because I connected signal to slots? (connect(this, SIGNAL(readyRead()), this, SLOT(getData()));)
    Thank you for your continued kindness :)


  • Lifetime Qt Champion

    @H-dragon
    Hi
    Yes its due to the readyRead signal calling getData() and fill Output.
    So we kinda mixed async and sync calls.
    But docs says
    "This function blocks until new data is available for reading and the readyRead() signal has been emitted. "
    https://doc.qt.io/qt-5/qserialport.html#waitForReadyRead

    so it does come in right order and then it worked.



  • @mrjj
    Hi. Thanks to you, I completed my GUI work. I really appreciate it.
    But while writing codes, I came up with a question.
    In your second answer, you changed Output = QIODevice::readAll() to Output += QIODevice::readAll().
    Can you explain why the former doesn't work(my gui terminate)? is it related with asynchronous?


  • Lifetime Qt Champion

    Hi
    Good to hear
    Well there is only a small difference

    This overwrites all that already is in Output
    Output = QIODevice::readAll()

    This Adds to what is already is in Output
    Output += QIODevice::readAll().

    The last is the best due to the following reason.

    When you sent something big enough over serial - it will result in multiple pieces of data
    so the readyRead() will trigger more than one time.

    So in such cases,
    one has to add the incoming data to a buffer as to collect it all

    • before using it.
      and that is what
      Output += QIODevice::readAll().
      does. Add data to the Output buffer.

    So that is something to keep in mind.



  • @mrjj
    Do I understand well?
    for example, let's say something big data (123456789) is divided into multiple pieces(123, 456, 789).

    If I use Output = QIODevice::readAll(), it can cause follow situation
    readyRead()
    Output is 123
    readyRead()
    Output is 456
    readyRead()
    Output is 789

    but when I use Output+=QIODevice::readAll(),
    readyRead()
    Output is 123
    readyRead()
    Output is 123456
    readyRead()
    Output is 123456789

    The reason += is the best is It can prevent the former situation. right?


  • Lifetime Qt Champion

    @H-dragon
    Hi
    Yes that is exactly like that.

    When it will be broken up depends on the hardware. some have bigger buffers than others and
    so on but it does happen.



  • Thanks!!


  • Lifetime Qt Champion

    @H-dragon

    Np.

    In your concrete use case, all feedback from the hardware ends with CR
    so its easy to spot when you have read all data.

    In other cases one has to look at size to know if
    all has been read.


Log in to reply