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

Problem with threads QObject::startTimer: Timers cannot be started from another thread



  • Hi,

    I'm having problem with QThread class. My application has two different threads: the main thread manage the GUI, the second thread sends and receives message on the SerialPort using QSerialPort.

    The problem is that when I start the second Thread I get this error:
    "QObject::startTimer: Timers cannot be started from another thread"

    I searched for this error on the net but I still can't find the problme on my code.
    Here is the code:

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include "newcard.h"
    #include "newcircuitnumber.h"
    #include "t_serialport.h"
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private slots:
        void on_buttonNewCard_clicked();
        void on_buttonNewImpiant_clicked();
    
    private:
        Ui::MainWindow      *ui;
        NewCard             *setNewCard;
        newCircuitNumber    *circuitNum;
        T_SerialPort        *serialObject;
    
        bool connectDB();
        void disconnect();
    };
    
    

    mainwindow.cpp

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        setWindowTitle("NFC");
    
        if(!connectDB())
        {                                
            qDebug() << "Error opening the db";
        }
        else
        {
            qDebug() << "db opened!";
        }
    
        serialObject = new T_SerialPort;
        serialObject->start();
    
        status_MFRC522 = 0;
    }
    

    t_serialport.h

    /#ifndef T_SERIALPORT_H
    #define T_SERIALPORT_H
    
    #include <QThread>
    #include <QSerialPort>
    
    
    typedef struct {
            uint8_t		size;			// Number of bytes in the UID. 4, 7 or 10.
            uint8_t		uidByte[10];
            uint8_t		sak;			// The SAK (Select acknowledge) byte returned from the PICC after successful selection.
    }UID_PICC;
    
    class T_SerialPort : public QThread
    {
        Q_OBJECT
        QSerialPort *portCom;
        void run();
    
    public:
        explicit T_SerialPort(QObject *parent = nullptr);
        ~T_SerialPort();
    
        
        bool startSearch = true;
    
        void MFRC522_AntennaOn();
        void MFRC522_Reset();
        void ReadRegister_MFRC522(uint32_t address, uint8_t *buff, uint32_t size);
        void WriteRegister_MFRC522(uint32_t address, uint8_t buff, uint32_t size, uint8_t mask);
        void MFRC522_ClearRegister(uint8_t reg, uint8_t mask);
        void MFRC522_SetRegister(uint8_t reg, uint8_t mask);
        void WriteRegisterBuffer_MFRC522(uint32_t address, uint8_t *buff, uint32_t size);
        int  MFRC522_CalculateCRC(uint8_t *data, uint8_t length, uint8_t *result);
        int  MFRC522_CommunicateWithTag(uint8_t command, uint8_t waitIRq, uint8_t *bufferToSend, uint8_t bufferToSendLen, uint8_t *bufferToReceive, uint8_t bufferToReceiveLen, uint8_t *validBits, uint8_t rxAlign, uint8_t CheckCRC);
        int  TAG_read(uint8_t *buffer_req, uint8_t buffer_len);
        int  PICC_IsNewCardPresent();
        void ReadRegisterBuffer_MFRC522(uint32_t address, uint8_t *buff, uint32_t size);
        UID_PICC 		UidPicc;
        int PICC_Select(	UID_PICC *uid, uint8_t validBits  );
        int PCD_Authenticate_Ultralight();
        void Tag_ReadInfo(uint8_t tagType);
        int MIFARE_Ultralight_Read(uint8_t page, uint8_t *buffer, uint8_t buffersize);
        int MIFARE_Read(uint8_t blockAddr, uint8_t *buffer, uint8_t bufferSize);
        int MIFARE_Ultralight_Write(uint8_t page, uint8_t *buffer, uint8_t bufferSize);
        int PCD_MIFARE_Transceive(uint8_t *sendData, uint8_t sendLen, bool acceptTimeout);
        void RicavaCredito();
        int PICC_HaltA();
        int PICC_GetType(uint8_t piccNum);
        uint8_t ControlloPicc(uint8_t tag);
        void Tag_WriteInfo(uint8_t tagType);
        uint8_t ScaloCredito();
    
    signals:
        void openPortOk(bool);
    
    };
    #endif // T_SERIALPORT_H
    

    t_serialport.cpp

    #include "t_serialport.h"
    #include "globs.h"
    
    #include <QDebug>
    #include <QThread>
    #include "globs.h"
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    STATUS  PICC_IsNewCardPresent(void);
    uint8_t ControlloStato(uint8_t status);
    
    T_SerialPort::T_SerialPort(QObject *parent) :
        QThread(parent)
    {
        portCom = nullptr;
        if(portCom == nullptr)
        {
            qDebug() << "CREO SERIAL PORT";
            portCom = new QSerialPort;
            portCom->setPortName("COM7");
            portCom->setFlowControl(QSerialPort::NoFlowControl);
            portCom->setBaudRate(QSerialPort::Baud9600);
            portCom->setDataBits(QSerialPort::Data8);
            portCom->setParity(QSerialPort::NoParity);
            portCom->setStopBits(QSerialPort::OneStop);
    //        connect(portCom, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
        }
        if(portCom->isOpen())
            portCom->close();
        if(portCom->open(QSerialPort::ReadWrite))
        {
            uint8_t prova;
            emit openPortOk(true);
            MFRC522_Reset();
            // When communicating with a PICC we need a timeout if something goes wrong.
            // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo].
            // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg.
            WriteRegister_MFRC522(TModeReg, 0x80, 2, 0xff);
            WriteRegister_MFRC522(TPrescalerReg, 0xA9, 2, 0xff);	// TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25�s.
            WriteRegister_MFRC522(TReloadRegH, 0x03, 2, 0xff);		// Reload timer with 0x2710 = 10000, ie 250ms before timeout.
            WriteRegister_MFRC522(TReloadRegL, 0xE8, 2, 0xff);
            WriteRegister_MFRC522(TxASKReg, 0x40, 2, 0x40);        // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
            WriteRegister_MFRC522(ModeReg, 0x3D, 2, 0x88);		// Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4)
            MFRC522_AntennaOn();    // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset
        }
        else
            emit openPortOk(false);
    
    }
    
    T_SerialPort::~T_SerialPort()
    {
        wait();
    }
    void T_SerialPort::run()
    {
        while(1)
        {
            uint8_t exit_timer, result, piccType;
    
            if(status_MFRC522 == 0)
            {
                qDebug() << "OK";
                int result = PICC_IsNewCardPresent();
                if(result == STATUS_OK || result == STATUS_COLLISION)
                {
                    qDebug() << "OK TROVATO QUALCOSA";
                    startSearch = false;
                    status_MFRC522 = 1;
                }
                else
                {
                    qDebug() << "NotFound";
                }
            }
            qDebug() << "PROVA";
            QThread::msleep(1000);
        }
    }
    int T_SerialPort::PICC_IsNewCardPresent(void){
        uint8_t bufferATQA[2];
        int result;
    
        result = TAG_read(&bufferATQA[0], sizeof(bufferATQA));                 // effettuo semplicemente una richeista di lettura se risponde uno o piu tag vuol dire che lo status è ok
        return (result == STATUS_OK || result == STATUS_COLLISION);
    }
    
    int T_SerialPort::TAG_read(uint8_t *buffer_req, uint8_t buffer_len){
        uint8_t validBits;
        int status;
        uint8_t byte;
    
    
        if((buffer_req == nullptr) || (buffer_len < 2)){
            qDebug() << "ERROR0";
            return CR_ERR;
        }
    
        MFRC522_ClearRegister(CollReg, 0x80);
        validBits = 7;
    
        byte = PICC_CMD_REQA;
        status = MFRC522_CommunicateWithTag(MFRC522_Transceive, 0x30, &byte, 1, buffer_req, buffer_len,&validBits,0, 0);      //THE PROGRAM STOPS HERE AND GIVE ME THE OUTPUT WRITTEN ABOVE
        if (status != STATUS_OK) {
                return status;
        }
    //	if (*buffer_req != 2 || validBits != 0) {		// ATQA must be exactly 16 bits.
    //			return STATUS_ERROR;                    //capire quanto deve essere il valid bits
    //	}
        return STATUS_OK;
    }
    int T_SerialPort::MFRC522_CommunicateWithTag(uint8_t command, uint8_t waitIRq, uint8_t *bufferToSend, uint8_t bufferToSendLen, uint8_t *bufferToReceive, uint8_t bufferToReceiveLen,uint8_t *validBits,uint8_t rxAlign ,uint8_t CheckCRC){
    
        uint16_t i;
        uint8_t n,_validBits;
        uint8_t errorRegValue;
        uint8_t txLastBits;
        uint8_t bitFraming;
    
        //*1 = se non funziona verificare con un a maschera diversa
        //*2 = Qui la maschera è diversa volutamente perchè il registro è dinamico q quando faccio partire il comando non so cosa fa nell'immediato
    
        txLastBits = validBits ? *validBits : 0;
        bitFraming = (rxAlign << 4) + txLastBits;
    
        WriteRegister_MFRC522(CommandReg, MFRC522_Idle, 2, 0x2F);   //*1             // Stop any active command.
        WriteRegister_MFRC522(ComIrqReg, 0x7F, 2, 0x00);	//1*                     // Clear all seven interrupt request bits
        MFRC522_SetRegister(FIFOLevelReg, 0x80);
        WriteRegisterBuffer_MFRC522(FIFODataReg, bufferToSend, bufferToSendLen);	 // Write sendData to the FIFO
        WriteRegister_MFRC522(BitFramingReg, bitFraming , 2, 0x77);		                         // Bit adjustments se settati a 0
        WriteRegister_MFRC522(CommandReg, command, 2, 0x00);	//*2			     // Execute the command
    
        if(command == MFRC522_Transceive){
            MFRC522_SetRegister(BitFramingReg, 0x80);	                                 // StartSend=1, transmission of data starts
        }
        // Wait for the command to complete.
        // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops transmitting.
        // Each iteration of the do-while-loop takes 17.86�s.
    
        i = 3000; //2000
        while (1)
        {
            ReadRegister_MFRC522(ComIrqReg, &n , 1);	// ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq
            if (n & waitIRq) {					// One of the interrupts that signal success has been set.
                break;
            }
            if (n & 0x01) {
                qDebug() << "QUITIMOUT1" <<  n;// Timer interrupt - nothing received in 25ms
                return STATUS_TIMEOUT;
            }
            if (--i == 0) {
                qDebug() << "QUITIMOUT2";// The emergency break. If all other condions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down.
                return STATUS_TIMEOUT;
            }
        }
    
        // Stop now if any errors except collisions were detected.
        ReadRegister_MFRC522(ErrorReg, &errorRegValue , 1); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr
        if (errorRegValue & 0x13) {	 // BufferOvfl ParityErr ProtocolErr
            qDebug() << "ERROR1" << errorRegValue << n << waitIRq;
            return STATUS_ERROR;
        }
    
        qDebug() << "VALORE" << n << "BUFFER TO RECEIVE" << bufferToReceiveLen;
        // If the caller wants data back, get it from the MFRC522.
        if (bufferToReceive && bufferToReceiveLen) {
            ReadRegister_MFRC522(FIFOLevelReg, &n , 1);	     		// Number of bytes in the FIFO
            if (n > bufferToReceiveLen) {
                return STATUS_NO_ROOM;
            }
            bufferToReceiveLen = n;											// Number of bytes returned
            ReadRegisterBuffer_MFRC522(FIFODataReg, bufferToReceive, n);	// Get received data from FIFO
    //        ReadRegisterBuffer_MFRC522(FIFODataReg, bufferToReceive, n);	// Get received data from FIFO
    
    
    
            ReadRegister_MFRC522(ControlReg, &_validBits,1);
    //        ReadRegister_MFRC522(ControlReg, &_validBits,1);
            _validBits &= 0x07;
    //		_validBits = PCD_ReadRegister(ControlReg) & 0x07;		// RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid.
            if (validBits) {
                *validBits = _validBits;
            }
        }
    
        // Tell about collisions
        if (errorRegValue & 0x08) {		// CollErr
            return STATUS_COLLISION;
        }
    
    
    //// Perform CRC_A validation if requested.
        if (bufferToReceive && bufferToReceiveLen && CheckCRC) {
            // In this case a MIFARE Classic NAK is not OK.
            if (bufferToReceiveLen == 1 && _validBits == 4) {
                return STATUS_MIFARE_NACK;
            }
            // We need at least the CRC_A value and all 8 bits of the last byte must be received.
            if (bufferToReceiveLen < 2 || _validBits != 0) {
                return STATUS_CRC_WRONG;
            }
            // Verify CRC_A - do our own calculation and store the control in controlBuffer.
            uint8_t controlBuffer[2];
            n = MFRC522_CalculateCRC(&bufferToReceive[0], (bufferToReceiveLen - 2), &controlBuffer[0]);
            if (n != STATUS_OK) {
                return n;
            }
            if ((bufferToReceive[bufferToReceiveLen - 2] != controlBuffer[0]) || (bufferToReceive[bufferToReceiveLen - 1] != controlBuffer[1])) {
                return STATUS_CRC_WRONG;
            }
        }
        return STATUS_OK;
    }
    void T_SerialPort::ReadRegister_MFRC522(uint32_t address, uint8_t * buff, uint32_t size){
    	uint8_t devadd;
        devadd = address | 0x80;
        
        portCom->write((char*)&devadd,1);
        if(portCom->waitForReadyRead())
        {
            QByteArray datareceived = portCom->readAll();
            qDebug() << "RICEVUTO" << datareceived;
            *buff = static_cast<uint8_t>(datareceived.at(0));
        }
    }
    /****************************************************************************************************************************************
     * Funzione dove scrivo nei registri del modulo.
     * address: è l'indirizzo a cui devo scrivere
     * buff: è il dato da scrivere
     * size: non la utilizzo
     * mask: indica dei vari registri i bit che posso effettivamente leggere perchè sono disponibili in lettura, quelli ceh sono diposnibili
     * solo in scrittura li trascuro
     ****************************************************************************************************************************************/
    void T_SerialPort::WriteRegister_MFRC522(uint32_t address, uint8_t buff, uint32_t size, uint8_t mask){
        uint8_t buffer [2];
        uint8_t buffer_2;
        uint8_t received;
    //    buffer[0] = (address << 1) & 0x7F;
        buffer[0] = address & 0x7F;     //l'ultimo bit deve essere settato a 0 per la scrittura
        buffer[1] = buff;
        buffer_2  = address | 0x80;     //l'ultimo bit deve essere settato a 1 per la lettura
    
        bool writecorrect = false;
        do{
            bool equal = false;
            do{
                portCom->write((char*)&buffer[0], 1);
                portCom->waitForBytesWritten(10);
                if(portCom->waitForReadyRead())
                {
                    QByteArray datareceived = portCom->readAll();
                    if(datareceived.at(0) != buffer[0])
                    {
                        qDebug() << "ERRORE DURANTE LA SCRITTURA" << datareceived.at(0) << address << datareceived.size();
                        if(datareceived.size() > 1)
                        {
                            qDebug() << "ALTRI DATI";
                            for(int k = 0; k < datareceived.size(); k++)
                            {
                                qDebug() << "DATO" << k << QString::number(datareceived.at(k));
                                if(datareceived.at(k) == buffer[1])
                                {
                                    equal = true;
                                    break;
                                }
                            }
                        }
                        if(datareceived.at(0) == buffer[1])
                        {
                            //vuol dire che ha sbagliato in qualche modo gli allinamenti e deve riallinearsi
                            portCom->write((char*)&buffer[0], 1);
                            if(portCom->waitForReadyRead())
                            {
                                QByteArray datareceived = portCom->readAll();
                                if(datareceived.at(0) != buffer[0])
                                {
                                    qDebug() << "ANCORA DIVERSI" << QString::number(datareceived.at(0)) << address << datareceived.size();
                                }
                                else
                                    equal = true;
                            }
                        }
                    }
                    else
                        equal = true;
                }
                portCom->write((char*)&buffer[1], 1);
                portCom->waitForBytesWritten(10);
            }while(!equal);     //a questo punto dovrei aver scritto il dato correttamente, vado a verificare eseguendo una lettura
    
            if(mask != 0x00)    // se il mask è uguale a 0x00 allora significa che nessuno dei bit del registro è disponibile in lettura quindi lo trascuro e setto writecorrect a true per farlo uscire dal ciclo
            {
                portCom->write((char*)&buffer_2, 1);
                if(portCom->waitForReadyRead())
                {
                   QByteArray datareceived = portCom->readAll();          //rileggo il registro che ho appena settato e controllo che effettivamente sia settato nel modo corretto
                   if((datareceived.at(0)&mask) == (buffer[1]&mask))      //qui suppongo di ricevere solo 1 byte dalla seriale, dovrebbe essere cosi, se ci sono più byte significa che c'è qualcosa che non va
                   {
                       writecorrect = true;
                       qDebug() << "Scritto correttamente";
                   }
                   else
                       qDebug() << "NON E SETTATO NEL MODO GIUSTO";
                }
                else
                    qDebug() << "NON HA RISPOSTO SULLA SERIALE";
            }
            else
                writecorrect = true;
    
        }while(!writecorrect);
    }
    

    I didn't put all the function because they're a lot, but if somebody needs additional code I can send the other.
    Basically the second thread starts and enter the while loop but it never reach the end and it gives me "QObject::startTimer: Timers cannot be started from another thread".

    I don't have timers in my program and I don't access the GUI from the other thread. Somebody can help me?
    Thanks in advance!


  • Qt Champions 2019

    You have to create your QSerialPort inside run() - otherwise the object is created in the main thread which is not what you want. And maybe QSerialPort is using a QTimer inside which results in the message you're seeing.



  • @Christian-Ehrlicher Thank you a lot! QSerialPort was the problem and moving it into the run() function made it works.


Log in to reply