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

Problem with QT and CAN (QCanBus and friends)



  • I'm writing an application that connects to a CAN bus and I found an issue that exists on the Windows platform and not the Linux platform. I found that the problem also exists in the CAN example program, so I will use that application to explain.

    Using the example 'CAN Bus Example'link text on a windows machine, when I click and hold the title bar of the application window, the application immediately stops receiving CAN frames from the hardware. At first I assumed that it was because the GUI thread was blocking the QCanBusDevice::framesReceived() signal, so in my application I created a separate 'worker thread' for receiving frames, but the same behavior exists. I looked into the source code and did some debugging and found that QCanBusDevice::readFrame() stops returning frames from the hardware when the title bar is clicked. I know this because I quit using the framesReceived() signal to read frames, and instead did a busy poll loop to read frames, both in the GUI Thread and in a separate thread. A simple example can be seen by running the example, connecting to some CAN hardware that spits out lots of CAN data continously, like an engine controller, and clicking on the title bar of the application. All of the CAN frames scrolling in the message window immediately stop on the windows platform. The readFrame() function does not block, it just returns empty frames as the docs say it should do when there are no CAN frames available. Meanwhile though, the hardwares receive queue starts filling with unread frames until I let go of the title bar.

    I have tested this on windows using Peak CAN hardware, Vector CAN hardware, and Tiny-CAN hardware. On Linux I have also tested this using the socketcan interface, using the same Peak and Tiny-CAN hardware and the problem does not exist. I click on the title bar of the application and drag it all over the desktop and CAN frames are still received with no interruptions.


  • Qt Champions 2020

    This is not a QCanBus issue. It is Windows issue.

    Most of QCanBus classes are used the qwineventnotifier which is stops when the user does click and hold the window title. It is a qwineventnotifier && windows issue/feature.

    Same behavior even belongs to all I/O Qt-classes relies on Windows's WriteFileEx/ReadFileEx functions, e.g. QLocalSocket, QProcess and so on.

    The TinyCan does not used the qwineventnotifier, it receives Rx callbacks from another thread, running inside of a native tiny-can library. So, I don't sure that on TinyCAN this ussue is reproduced too.

    As a workaround, just move your QCanBusDevice's instances to other thread (seems it is possible).



  • I've created a new thread where I create the device and connect:

    m_canDevice1 = QCanBus::instance()->createDevice("peakcan", "usb0", &errorString);
    if (!m_canDevice1) {
            //emit connect_status(1, errorString);
            return;
        }
    if (!m_canDevice1->connectDevice()) {
            //emit connect_status(1, m_canDevice1->errorString());
            delete m_canDevice1;
            m_canDevice1 = nullptr;
        }
    

    (connect_status signal is commented out for now during debug)

    After the connectDevice() call I get:

    QObject::startTimer: Timers cannot be started from another thread
    
    

    and of course no CAN functionality. I have taken all QCanBus* code out of the GUI thread so there are no instances of the hardware in the GUI thread anymore.


  • Qt Champions 2020

    @nostar said in Problem with QT and CAN (QCanBus and friends):

    QObject::startTimer: Timers cannot be started from another thread

    This means that you use threads wrong. Please read documentation about using a threads.



  • Here is the complete CANThread class. How am I using threads wrong??

    The thread is started from MainWindow via a connect button handler:

            can = new CANThread();
            connect(can, SIGNAL(data_read1()), this, SLOT(process_received1()));
            connect(can, &CANThread::finished, can, &QObject::deleteLater);
            can->start();
    

    canthread.h

    #ifndef CANTHREAD_H
    #define CANTHREAD_H
    
    #include <QObject>
    #include <QThread>
    #include <QQueue>
    #include <QCanBusDevice>
    #include <QCanBusFrame>
    
    
    class CANThread : public QThread
    {
        Q_OBJECT
    public:
        CANThread();
        ~CANThread();
        void run();
        QCanBusFrame rx1;
        uint8_t connected;
        QQueue<QCanBusFrame> rxdata;
    
    signals:
        //void connect_status(char s);
        void data_read1();
    
    private:
        QCanBusDevice *m_canDevice1 = nullptr;
    };
    
    #endif // CANTHREAD_H
    

    canthread.cpp

    #include "canthread.h"
    #include <QCanBus>
    #include <QVariant>
    
    CANThread::CANThread()
    {
    
    }
    
    CANThread::~CANThread()
    {
    
    }
    
    void CANThread::run()
    {
    
        QString errorString;
        m_canDevice1 = QCanBus::instance()->createDevice("peakcan", "usb0", &errorString);
    
        if (!m_canDevice1) {
            //emit connect_status(1, errorString);
            return;
        }
    
        m_canDevice1->setConfigurationParameter(QCanBusDevice::CanFdKey, canfd);
    
        if (!m_canDevice1->connectDevice()) {
            //emit connect_status(1, m_canDevice1->errorString());
            delete m_canDevice1;
            m_canDevice1 = nullptr;
        }
        else{
            QVariant bitRate = m_canDevice1->configurationParameter(QCanBusDevice::BitRateKey);
            connected = 1;
            //emit connect_status(0, NULL);
        }
    
        while(connected){
           while (m_canDevice1->framesAvailable()) {
                rx1 = m_canDevice1->readFrame();
                if(rx1.frameId()){
                    rxdata.enqueue(rx1);
                    emit data_read1();
                }
            }
        }
        m_canDevice1->disconnectDevice();
        delete m_canDevice1;
        m_canDevice1 = nullptr;
    }
    

  • Qt Champions 2020

    @nostar said in Problem with QT and CAN (QCanBus and friends):

    How am I using threads wrong??

    Here all is wrong! Please read documentation about (these are tips for you):

    1. QObject's ownerships.
    2. QObject's move to thread.

    PS: You can found all required info in google or on this forum. Similar issues were talked 100500 times.


Log in to reply