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

QSerialPort transmit errors while sending 0xff



  • Hello,

    i try to implement a communication between an embedded device and my computer. During development, i observed that i get a lot errors during the communication, when the byte array contains magic bytes like 0xff.
    It got more strange when i tried to send only characters like 0xff. On my serial adapter i can see, that the TX LED stars stuttering. If i send other bytes like 0x55 or something like that, the LED is smoothly flashing and i have no errors during transmit.
    It is really easy to reproduce and to prove that my target has no influence. I disconnected my target and i can still see that the LED stutters during 0xff and not during other characters on my serial adapter.
    To make it more confusing: The majority of bytes is transmitted correctly. Just a small percentage of 0xff's is not transmitted correctly.
    I am using Mac OS Mojave and tried it with Qt 5.6 and 5.12. Both show exactly the same behavior.

    Has someone an idea from where this is coming and how i can fix this?

    Greetings
    Tobias


  • Lifetime Qt Champion

    Hi @tobias423412

    I'm using QtSerialPort to transmit binary data to one of my self-built embedded devices using FTDI serial adapters - without problems.

    So there are three possible things that could be wrong on your side:

    1. Your hardware - which is that?
    2. Your code - can you show it?
    3. Are you using a software protocol like XON/XOFF?

    Regards



  • Hi,

    this is not the most clean code, but it does the job:

    #include "serialportwriter.h"
    
    #include <QCoreApplication>
    #include <QDebug>
    #include <QThread>
    QT_USE_NAMESPACE
    #define TEST_BYTE ((uint8_t)0xff)
    #define END_ADDR 0xf
    
    SerialPortWriter::SerialPortWriter(QSerialPort *serialPort, QObject *parent)
        : QObject(parent)
        , m_serialPort(serialPort)
        , m_standardOutput(stdout)
        , m_bytesWritten(0)
        , m_testByte(TEST_BYTE)
    {
        m_timer.setSingleShot(false);
        connect(m_serialPort, &QSerialPort::bytesWritten, this, &SerialPortWriter::handleBytesWritten);
        connect(m_serialPort, static_cast<void (QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
                this, &SerialPortWriter::handleError);
        connect(&m_timer, &QTimer::timeout, this, &SerialPortWriter::handleTimeout);
        connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPortWriter::readReady);
        m_timer.start(35);
    }
    
    SerialPortWriter::~SerialPortWriter()
    {
    }
    
    void SerialPortWriter::handleBytesWritten(qint64 bytes)
    {
    
    }
    
    void SerialPortWriter::handleTimeout()
    {
        static bool read = false;
        static uint32_t readCycle = 0;
        static uint32_t writeCycle = 0;
        static uint16_t addr = 0;
        if (read)
        {
            QByteArray readData;
            readData.push_back((char)0x1); // Read Command
            readData.push_back((char)(addr >> 8)); // 23:16
            readData.push_back((char)(addr & 0xff)); // 15:8
            readData.push_back((char)0x0); // 7:0
            readData.push_back((char)0x40); // Size in DWord
    
    
            if (addr < END_ADDR)
            {
                write(readData);
                addr++;
            }
            else
            {
                addr = 0;
                read = false;
                qDebug() << "Read cylce" << readCycle++;
            }
        }
        else
        {
            QByteArray writeData;
            writeData.push_back((char)0x2); // Write Command
            writeData.push_back((char)(addr >> 8)); // 23:16
            writeData.push_back((char)(addr & 0xff)); // 15:8
            writeData.push_back((char)0x0); // 7:0
            writeData.push_back((char)0x40); // Size in DWord
    
            // Payload
            for (int i = 0; i < 0x40; i++)
            {
                writeData.push_back((char)m_testByte);
                writeData.push_back((char)m_testByte);
                writeData.push_back((char)m_testByte);
                writeData.push_back((char)m_testByte);
            }
    
            if (addr < END_ADDR)
            {
                write(writeData);
                addr++;
            }
            else
            {
                qDebug() << "Write cylce" << writeCycle++;
                addr = 0;
                read = true;
            }
        }
    }
    
    void SerialPortWriter::handleError(QSerialPort::SerialPortError serialPortError)
    {
        if (serialPortError == QSerialPort::WriteError) {
            m_standardOutput << QObject::tr("An I/O error occurred while writing the data to port %1, error: %2").arg(m_serialPort->portName()).arg(m_serialPort->errorString()) << endl;
            QCoreApplication::exit(1);
        }
    }
    
    void SerialPortWriter::readReady()
    {
        QByteArray r = m_serialPort->readAll();
        for (int i = 0; i < r.size(); i++)
        {
            if ((uint8_t)(r.at(i)) != m_testByte)
            {
                qDebug() << "Read Error actual:" << QString::number((uint8_t)(r.at(i))).toInt() << "should:" << QString::number(m_testByte).toInt();
            }
        }
    }
    
    void SerialPortWriter::write(const QByteArray &writeData)
    {
        m_writeData = writeData;
    
        qint64 bytesWritten = m_serialPort->write(writeData);
    
        if (bytesWritten == -1) {
            m_standardOutput << QObject::tr("Failed to write the data to port %1, error: %2").arg(m_serialPort->portName()).arg(m_serialPort->errorString()) << endl;
            QCoreApplication::exit(1);
        } else if (bytesWritten != m_writeData.size()) {
            m_standardOutput << QObject::tr("Failed to write all the data to port %1, error: %2").arg(m_serialPort->portName()).arg(m_serialPort->errorString()) << endl;
            QCoreApplication::exit(1);
        }
        //m_serialPort->flush();
    }
    

    What i am doing here? I write 0xff in some memory and when i am done with this, i try to read it back. And that is what i am doing forever.
    If i am using for TEST_BYTE 0xAA or something like that, everything works fine. If it is 0xff than something goes wrong. If i read the memory back, i get errors on random places.
    The error outputs like this

    Read Error actual: 251 should: 255
    Read Error actual: 254 should: 255
    Read Error actual: 251 should: 255
    Read Error actual: 254 should: 255
    Read Error actual: 251 should: 255
    

    As a serial converter i am using an ordinary FT232.

    Greetings
    Tobias


  • Lifetime Qt Champion

    @tobias423412

    Your code still looks a bit complicated and is missing important pieces, like the serial port set up. Which baud rate are you using? Am I right that you repeat that cycle every 35 ms?

    On my serial adapter i can see, that the TX LED stars stuttering.

    Ok, then I'd start with a simple program that only transmits a fixed frame, containing 0xff. Does that work? If yes, then do it from a timer with a timeout large enough that the previous frame is completely transmitted (to avoid buffer overflows).

    I really can't see why transmitting arbitrary data should lead to problems like you described.

    Regards



  • Hi,

    yes, the init

    #include "serialportwriter.h"
    
    #include <QtSerialPort/QSerialPort>
    #include <QCoreApplication>
    
    #include <QString>
    #include <QDebug>
    
    QT_USE_NAMESPACE
    
    int main(int argc, char *argv[])
    {
    
        QCoreApplication coreApplication(argc, argv);
        int argumentCount = QCoreApplication::arguments().size();
        QStringList argumentList = QCoreApplication::arguments();
    
        QTextStream standardOutput(stdout);
    
        if (argumentCount != 3) {
            standardOutput << "Version 1.0.0 (Compile date: " << __DATE__ << " " << __TIME__ << ")" << endl;
            standardOutput << QObject::tr("Usage: %1 <serialportname> <baudrate>").arg(argumentList.first()) << endl;
            return 1;
        }
    
        QSerialPort serialPort;
        QString serialPortName = argumentList.at(1);
        serialPort.setPortName(serialPortName);
    
        int serialPortBaudRate = argumentList.at(2).toInt();
        serialPort.setBaudRate(serialPortBaudRate);
    
        serialPort.open(QIODevice::ReadWrite);
        serialPort.setParity(QSerialPort::NoParity);
        serialPort.setStopBits(QSerialPort::OneStop);
        serialPort.setFlowControl(QSerialPort::NoFlowControl);
        serialPort.setDataBits(QSerialPort::Data8);
    
        SerialPortWriter serialPortWriter(&serialPort);
        return coreApplication.exec();
    }
    
    

    I am using 115200 baud.

    I use that timer to periodically send one frame of data. After every 15 timer shots i switch from sending payload to reading payload. Theoretically i can send around 500 bytes within this 35ms and currently sending around 261.

    I tried also now to just send a byte array containing only 0xff. I also increased the time of the timer. The TX LED on the FT232 is still stuttering.

    Greetings
    Tobias



  • I guess i found a solution which i don't like.

    I observed also two crashes of my USB stack on my Mac which are (obviously) related to this stuff. Sometimes when i start this program all my USB ports are dying and the kernel tasks runs with 100% load. I can only resurrect the USB ports by a reboot of my computer. If i connect the serial adapter on a nativ USB port, it works as intended without any errors.

    The USB Hub should have enough power. It is a Hub which is powered from an external power plug.

    For sure, i would be really thankful if anyone know a better workaround to this bug instead of utilizing one of the very rare native USB ports on a Mac, but in this case, i guess, it is an Apple problem and not an Qt related one.

    Thank you very much for your help

    Greetings
    Tobias


Log in to reply