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

For multiple sensors in RS485 hub, send and receive data cyclically



  • Hi, I have some issues with reading and sending data for multi-sensors in an RS485 hub 16 port. In my project, I need to send and read data from multiple different sensors at the same time and show it on the screen. The frequency of these sensors is all at 10 Hz. I need to send commands according to the protocol and wait for the return of the data.

    1. I use QtConcurrent::run() to Send commands and wait for data coming continuously in a loop for each sensor. But I found when I sending these commands in parallel can easily cause sending failures. I seem to have to send them at a sufficiently long interval each time to guarantee the success of the sending. I know this may be caused by too fast writing to the serial port. Send these instructions at the same point in time. So are there any solutions that can solve it?

    timeout.png

    1. I am very entangled in whether to put the read data in a separate thread for processing, or to put the read data and send commands in the same thread (waiting for instructions after sending). If put them into different threads, I feel this part of the processing will be more troublesome when the received data is incomplete or missing, or when the data is classified according to different sensors.
    // Each sensor starts a thread
    QtConcurrent::run(port,&Serial_Service::SendReceiveData,Button_Index,Type_Index,ID.toInt());
    
    void Serial_Service::loopSendRecive(int Reader_Index, int Type_Index, int ID)
    {
        // According to the different type of sensors, getting the command to send
        QByteArray TxBuffer = Update_Send_Data(Type_Index,ID);
    
        m_isCanRead[Reader_Index-1] = true;
    
        // start loop
        while(m_isCanRead[Reader_Index-1])
        {
            m_lock.lock();
    
            //send the data
            const qint64 bytesWritten = port->write(TxBuffer);
    
            qApp->processEvents();
    
            if(!port->waitForBytesWritten())
            {
                qDebug()<<QString("serial write failed because: %1").arg(port->errorString());
            }
            // wait for the data coming and processing
            ulong use_time = waitforData(Reader_Index,Type_Index,ID);
            m_lock.unlock();
    
            QThread::msleep(100);
        }
    }
    
    ulong Serial_Service::waitforData(int Reader_Index, int Type_Index, int ID)
    {
        // different sensors have different Frame;
        QByteArray startOfFrame;
    
        int Read_Length = 0;
    
        switch (Type_Index) {
        case HCA726S:{
            startOfFrame = QByteArray::fromHex("68");
            Read_Length = 16;
            break;
        }
        case LVDT_SENSORS:{
            startOfFrame = QByteArray::fromHex("01");
            qDebug()<<"LVDT";
            Read_Length = 9;
            break;
        }
        case TORQUE_SENSORS:{
            startOfFrame = QByteArray::fromHex("BB");
            Read_Length = 10;
            break;
        }
        }
    
        QByteArray responseData;
    
        // Sometimes the code will block here, I don’t know why this happens
        if(port->bytesAvailable() != -1)
        {
            while(port->bytesAvailable()==0)
            {
                QThread::msleep(5);
            }
            // if data can be read
            if(port->bytesAvailable()!=0)
            {
                QByteArray temp = port->read(1);
    
                // Find the start of the frame
                while (temp != startOfFrame) {
                    temp = port->read(1);
                }
                responseData.clear();
    
                responseData.append(temp);
    
                // If the data is incomplete, wait for the data
                while(port->bytesAvailable()<Read_Length-1)
                {
                    QThread::msleep(5);
                }
    
                responseData.append(port->read(Read_Length-1));
    
                emit Send_Data_to_Process(responseData,Reader_Index);
            }
        }
    }
    

  • Lifetime Qt Champion

    Why using threads in the first place for no reason? Don't use them but signals and slots and all your problems and locking stuff can be thrown away.



  • Thank you so much for your suggestion. I have modified the code according to your suggestion and deleted all unnecessary threads. Now I put the serial port sending in a continuous looping thread, and the response of the button is just to add the command to be sent, and reading the data through readyRead(). But there are still some issues.

    1. When sending multiple different commands, it seems that partial data loss. But when sending the same commands, this will happen.

    2. Would it be a good choice to use asynchronous sending? I have referred to the asynchronous sending tutorial. But how to achieve asynchronous sending in a loop?

    button click

    if(Component_List[i].Button_Read->text() == tr("Read"))
    {
          Component_List[i].Button_Read->setText("Stop");
           port->SetRequest(Type_Index,ID.toInt());
    }
    

    Add command

    void Serial_Service::SetRequest(int Type_Index, int ID)
    {
        QByteArray request = Update_Send_Data(Type_Index, ID);
        // get the command and append it to the QByteArrayList
        m_writeData.append(request);
        m_bRunThread = true;
    }
    

    The thread of sending

    void Serial_Service::loopSend()
    {
        m_bRunThread = true;
        while(m_bRunThread)
        {
            if(!m_writeData.isEmpty())
            {
                qDebug()<<"The length of write data:"<<m_writeData.length();
                for(int i=0;i<m_writeData.length();i++)
                {
                    qint64 bytesWritten = port->write(m_writeData[i]);
                    qDebug()<<"index i: "<<i<<"write data:"<<m_writeData[i].toHex();
                    if (!port->waitForBytesWritten(5000)) {
                        qDebug() << QObject::tr("Operation timed out or an error occurred for port %1, error: %2").arg(port->portName()).arg(port->errorString()) << endl;
                    }
                    // Without this line, sending multiple different commands will not succeed
                    QThread::msleep(1);
                }
                qDebug()<<QString(QDateTime::currentDateTime().toString("hh:mm:ss.zzz"));
                QThread::msleep(Interval);
            }
        }
    }
    

    Reading

    connect(port, SIGNAL(readyRead()), this, SLOT(RecieveData()));
    
    void Serial_Service::RecieveData()
    {
            static QByteArray Rx_buffer;
            Rx_buffer.append(port->readAll());
            qDebug()<<"Rx_buffer:"<<Rx_buffer.toHex();
    }
    

    Snipaste_2021-03-15_12-24-53.png


  • Lifetime Qt Champion

    @Yandong-Luo said in For multiple sensors in RS485 hub, send and receive data cyclically:

    while(m_bRunThread)

    You're blocking event loop.
    Is Serial_Service running in its own thread?
    Why don't you use QTimer to send data periodically instead of using an event loop blocking loop?



  • @jsulm Really thanks for your suggestion. I now use QTimer instead of using an event loop blocking loop. But when two or more different commands are sent continuously, the returned data is messy, wrong, or incomplete. I tested, if I set a sufficient sleep interval (about 50 milliseconds) between each command, all returned data will return to normal. But this sleep time will affect the simultaneous reading of these sensors. As the data to be sent increases, these sleep times will gradually accumulate.

    In other words, how can I send multiple requests to an RS485 hub at the same time as possible and get the correct data?


  • Lifetime Qt Champion

    @Yandong-Luo said in For multiple sensors in RS485 hub, send and receive data cyclically:

    how can I send multiple requests to an RS485 hub at the same time as possible and get the correct data?

    I suggest to implement a protocol: you send one request and wait for a response from the receiver, then you send next request. You simply can't send two requests at the "same time".



  • @jsulm Thanks for your help!



  • @Yandong-Luo You may send a lot of requests one after another quickly. But you may cache the return data( push them into a queue and process them one after another).



  • Serenity I appreciate all of your diligent work in helping me solve my problem. Finally, I adopted the scheme from Juslm. I implement a protocol: I send one request and wait for a response from the receiver, then I send the next request. The time required for each sending and receiving is about 40ms.


Log in to reply