4

I am trying to read the data sent by a device plug via usb. First i read the data via this command

  • sudo stty -F /dev/ttyUSB0 1200 sane parenb evenp cs7 -crtscts
  • cat /dev/ttyUSB0

And the data are like this

TGPHI_s -0,24 =

MESURES2 BT 4 SUP36 A

PTCOUR2 HPH /

Now i want to read the data via a Qt5.3 program

QSerialPort serial;
serial.setPortName("ttyUSB0");
if(!serial.setBaudRate(QSerialPort::Baud1200 , QSerialPort::Input))
    qDebug() << serial.errorString();
if(!serial.setDataBits(QSerialPort::Data7))
    qDebug() << serial.errorString();
if(!serial.setParity(QSerialPort::EvenParity))
    qDebug() << serial.errorString();
if(!serial.setFlowControl(QSerialPort::HardwareControl))
    qDebug() << serial.errorString();
if(!serial.setStopBits(QSerialPort::OneStop))
    qDebug() << serial.errorString();
if(!serial.open(QIODevice::ReadOnly))
    qDebug() << serial.errorString();
qDebug() << serial.bytesAvailable();
while(true)
{
    if (serial.isOpen()) {
        qDebug() << "Serial port is open...";
        QByteArray datas = serial.readAll();
        if (datas.size() == 0) {
            qDebug() << "Arrived data: 0";
        } else {
            for (int i = 0; i < datas.size(); i++){
                if (datas.at(i)) {
                    qDebug() << datas[i];
                }
            }
        }

    } else {
        qDebug() << "OPEN ERROR: " << serial.errorString();
    }
}
return 0;

and the answer is ->

"/dev/ttyUSB0"
0
Serial port is open...
Arrived data: 0
Serial port is open...
Arrived data: 0

So there is no data catch by my program ... My questions are :

  • Did i miss something in the setting of the QSerialPort ?
  • If no why there is no data display via the qDebug()

EDIT

Thanks to mike i can finaly read this usb device !!! here is my final code

    QSerialPort serial;
serial.setPortName("ttyUSB0");
if(!serial.setBaudRate(QSerialPort::Baud1200))
    qDebug() << serial.errorString();
if(!serial.setDataBits(QSerialPort::Data7))
    qDebug() << serial.errorString();
if(!serial.setParity(QSerialPort::EvenParity))
    qDebug() << serial.errorString();
if(!serial.setFlowControl(QSerialPort::HardwareControl))
    qDebug() << serial.errorString();
if(!serial.setStopBits(QSerialPort::OneStop))
    qDebug() << serial.errorString();
if(!serial.open(QIODevice::ReadOnly))
    qDebug() << serial.errorString();
QObject::connect(&serial, &QSerialPort::readyRead, [&]
{
    //this is called when readyRead() is emitted
    //qDebug() << "New data available: " << serial.bytesAvailable();
    qDebug() << "New data available: " << serial.bytesAvailable();
    QByteArray datas = serial.readAll();
    qDebug() << datas;
});
QObject::connect(&serial,
                     static_cast<void(QSerialPort::*)(QSerialPort::SerialPortError)>
                     (&QSerialPort::error),
                     [&](QSerialPort::SerialPortError error)
{
    //this is called when a serial communication error occurs
    qDebug() << "An error occured: " << error;
    return qApp->quit();
});


if(!serial.open(QIODevice::ReadOnly))
    qDebug() << serial.errorString();
return qApp->exec();
tlebreton
  • 43
  • 1
  • 1
  • 5

1 Answers1

9

Most IO functions in Qt are asynchronous. This means that readAll() does not wait for data to arrive. Instead, it returns currently available data (data that can be read from the device without waiting). Currently, You are just calling readAll in an endless loop (this makes the thread, spend all its time in this loop, unable to receive the new data that may have arrived. . .)

You need to call readAll only when you know that new data has arrived. This can be accomplished in two ways:

Non-blocking Asynchronous Way:

Use the readyRead() signal to get notified when new data is available in the device instead of looping forever. This is how you should do most stuff in Qt, In order to be able to act upon multiple events that may arrive at any time. Your code can be rewritten like this:

    #include <QtSerialPort>

    int main(int argc, char* argv[]){
        QCoreApplication a(argc, argv);
        QSerialPort serial;
        serial.setPortName("ttyUSB0");
        if(!serial.setBaudRate(QSerialPort::Baud1200))
            qDebug() << serial.errorString();
        if(!serial.setDataBits(QSerialPort::Data7))
            qDebug() << serial.errorString();
        if(!serial.setParity(QSerialPort::EvenParity))
            qDebug() << serial.errorString();
        if(!serial.setFlowControl(QSerialPort::HardwareControl))
            qDebug() << serial.errorString();
        if(!serial.setStopBits(QSerialPort::OneStop))
            qDebug() << serial.errorString();
        if(!serial.open(QIODevice::ReadOnly))
            qDebug() << serial.errorString();
        QObject::connect(&serial, &QSerialPort::readyRead, [&]
        {
            //this is called when readyRead() is emitted
            qDebug() << "New data available: " << serial.bytesAvailable();
            QByteArray datas = serial.readAll();
            qDebug() << datas;
        });
        QObject::connect(&serial,
                         &QSerialPort::errorOccurred,
                         [&](QSerialPort::SerialPortError error)
        {
            //this is called when a serial communication error occurs
            qDebug() << "An error occured: " << error;
            a.quit();
        });

        return a.exec();
        //     ^^^^^^^^
        //very important: starts the Qt main event loop
        //this makes all asynchronous stuff possible
    }

Blocking Synchronous Way:

Use waitForReadyRead() to block the thread until new data arrives to the serial port. This makes the calling thread unable to do anything until new data arrives on this serial port. If this thread was a GUI thread, This will make the application unresponsive during that period of time. Use this approach only when you are sure this is what you want. Your code can be rewritten like this:

    #include <QtSerialPort>

    int main(int argc, char* argv[]){
        QCoreApplication a(argc, argv);
        QSerialPort serial;
        serial.setPortName("ttyUSB0");
        if(!serial.setBaudRate(QSerialPort::Baud1200))
            qDebug() << serial.errorString();
        if(!serial.setDataBits(QSerialPort::Data7))
            qDebug() << serial.errorString();
        if(!serial.setParity(QSerialPort::EvenParity))
            qDebug() << serial.errorString();
        if(!serial.setFlowControl(QSerialPort::HardwareControl))
            qDebug() << serial.errorString();
        if(!serial.setStopBits(QSerialPort::OneStop))
            qDebug() << serial.errorString();
        if(!serial.open(QIODevice::ReadOnly))
            qDebug() << serial.errorString();
        qDebug() << serial.bytesAvailable();
        while(serial.isOpen())
        {
            if(!serial.waitForReadyRead(-1)) //block until new data arrives
                qDebug() << "error: " << serial.errorString();
            else{
                qDebug() << "New data available: " << serial.bytesAvailable();
                QByteArray datas = serial.readAll();
                qDebug() << datas;
            }
        }
        return 0;
    }
Mike
  • 8,055
  • 1
  • 30
  • 44
  • Thanks for your reponse but there is allways the problem that there is no data display. I take the asynchronous way and the only think i got is that. http://pastebin.com/RW7PVNMN With a blank between each after New data available: 1. Maybe that come from the parameter (baudRate, databits ...). Do you think that match the option from the stty command? – tlebreton Mar 03 '17 at 13:02
  • @tlebreton , I have changed the way output is done, this can give better representation of received data (and hence help figure out what is going on). I think the parameters are identical. Maybe try using `serial.setStopBits(QSerialPort::TwoStop)` instead of one stop bit, because I am not sure about how `sane` sets that in `stty`. – Mike Mar 03 '17 at 13:40
  • I found my mistake (shame on me). In the setBaudRate i dont need the QSerialPort::Input. With there is nothink shown and without i got all i want Thanks for the help mike i appreciate – tlebreton Mar 03 '17 at 14:31
  • @tlebreton, You are welcome. I don't understand why would that cause problems though. AFAIK, this means that the baud rate setting is applied only on input data (i.e., on data that is being read), and that should be what we are aiming for, isn't it? Anyway, I have updated the answer :) – Mike Mar 03 '17 at 14:39
  • @Mike I'm having the following error with the Asynchronous way: `invalid 'static_cast' from type 'QSerialPort::SerialPortError (QSerialPort::*)() const' to type 'void (QSerialPort::*)(QSerialPort::SerialPortError)'` can you help me? – ruda Feb 07 '23 at 00:09
  • @ruda, You are right. The signal `QAbstractSocket::error` has been removed in Qt6, and was superseded by `QAbstractSocket::errorOccurred`. This removes the need for the ugly cast. See my updated answer... – Mike Apr 07 '23 at 17:05