Search code examples
c++qtserial-portqthread

QSerialPort - wating for whole data from sender


I'm working with a serial device. The QSerialPort is in a separate thread. Thread is created this way:

QThread* serialthread = new QThread;
Serial*  serial = new Serial();
serial->moveToThread(serialthread);

When Data is available this signal in my thread worker is emited:

    void Serial::process()
    {
        serialport = new QSerialPort();
        connect(this->serialport,SIGNAL(readyRead()),this,SLOT(readyToRead()));
    }
    void Serial::readyToRead()
    {
        emit SIG_dataAvailable(this->read());
    }

This is the function that reads the data and checks if the data is correct - the second byte on my serial device says how long the rest of the packet is...

QByteArray Serial::read() const
{
    QByteArray receivedData;
    int length;
    receivedData = serialport->readAll();
    length = receivedData[1];
    if(length != receivedData.length() - 1)
    {
        qDebug() << "protocol error.";
        return NULL;
    }
    return receivedData;
}

My problem is that the signal QSerialPort::readyRead is emited before the data from the serial device is complete in the buffer. Any idea how to solve this problem?


Solution

  • There is absolutely NO guarantee that you'll get whole data at ONCE. You can solve this problem in some ways.

    1) If you have fixed size package you can do something like this:

    void foo::onSerialRead()
    {
        //! Is there whole datagram appears?
        if (m_serial->bytesAvailable() < ::package_size) {
            //! If not, waiting for other bytes
            return;
        }
    
        //! Read fixed size datagram.
        QByteArray package = m_serial->read(::package_size);
        //! And notify about it.
        emit packageReady(package);
    }
    

    2) If your package size may vary. Then you have to include "hader" in to your package. This header should contain at least "start" byte and data size (Its second byte in your case). And header shuld be fixed size. Then you can do something like this:

    void foo::onSerialRead()
    {
        static QByteArray package;
        static bool isHeaderRead = false;
        static quint8 startByte = 0;
        static quint8 dataSize = 0;
    
        //! Is there whole header appears?
        if (m_serial->bytesAvailable() < ::header_size) {
            //! If not, waiting for other bytes
            return;
        }
    
        if (!isHeaderRead) {
        //! Read fixed size header.
            package.append(m_serial->read(::header_size));
            QDataStream out(&package);
    
            out >> startByte;
    
        //! Check is it actually beginning of our package?
            if (Q_UNLIKELY(startByte != ::protocol_start_byte)) {
                return;
            }
            out >> dataSize;
            isHeaderRead = true;
        }
    
        //! Check is there whole package available?
        if (Q_LIKELY(dataSize > m_serial->bytesAvailable())) {
            //! If not, waiting for other bytes.
            return;
        }
        //! Read rest.
        package.append(m_serial->read(dataSize));
        //! And notify about it.
        emit packageReady(package);
        package.clear();
        isHeaderRead = false;
    }
    

    And there is absolutely no point in putting your QSerial in to different thread.