簡體   English   中英

Qt串行端口 - 一致地讀取數據

[英]Qt Serial Port - Reading data consistently

我通過串口向設備發送(寫入)字節。 我正在使用QSerialPort( http://qt-project.org/wiki/QtSerialPort )模塊來實例化設備IO支持。 當我向INSTEON調制解調器(串行)發送消息時,在讀取我的消息時,設備會發回我的消息副本+ 0x06(ACK Byte),然后是狀態消息。

我使用DockLight( http://www.docklight.de/ )測試了我的消息。 我發送以下消息來查詢設備的狀態:

    02 62 1D E9 4B 05 19 00

使用Docklight,我收到回復:

    02 62 1D E9 4B 05 19 00 06 02 50 20 CB CF 1E DA F7 21 00 FF

返回的消息准確表明了我期望的設備是什么。 如果關閉,如果設備關閉,調制解調器將在最后一個字節位置發回0x00。 現在,我的問題 - 我必須正確設置我的功能才能發送然后接收響應字節。 我嘗試了很多不同的示例和配置,目前我正在使用以下內容:

設置信號槽連接:

QObject::connect(&thread, SIGNAL(sendResponse(QByteArray)), 
    this, SLOT(handleResponse(QByteArray)));
QObject::connect(&thread, SIGNAL(error(QString)), 
    this, SLOT(processError(QString)));
QObject::connect(&thread, SIGNAL(timeout(QString)), 
    this, SLOT(processTimeout(QString)));

用於迭代設備QList的函數。 如果設備是所需類型(“Light”),那么我們將設備ID格式化為預期的QByteArray消息結構。 將消息傳遞給線程以進行發送。 (從QSerialPort BlockingMaster示例修改的線程。

void Device::currentStatus(QList<Device *> * deviceList){
    QString devID, updateQry;
    int devStatus, updateStatus;
    updateStatus=0;
    QSqlQuery query;
    for(int i=0; i<deviceList->size(); i++){
        if(deviceList->at(i)->type == "Light"){
            devStatus = deviceList->at(i)->status;
            devID = deviceList->at(i)->deviceID;
            QByteArray msg;
            bool msgStatus;
            msg.resize(8);

            msg[0] = 0x02;
            msg[1] = 0x62;
            msg[2] = 0x00;
            msg[3] = 0x00;
            msg[4] = 0x00;
            msg[5] = 0x05;
            msg[6] = 0x19;
            msg[7] = 0x00;
            msg.replace(2, 3, QByteArray::fromHex( devID.toLocal8Bit() ) );
            qDebug() << "Has device " << deviceList->at(i)->name << "Changed?";
            //send(msg,&msgStatus, &updateStatus);
            //msg.clear();
            thread.setupPort("COM3",500,msg);
            if(devStatus!=updateStatus){
                qDebug() << deviceList->at(i)->name << " is now: " << updateStatus;
                updateStatus = !updateStatus;
            }
        }
    }
}

SetupThread函數用於設置本地線程變量並執行(運行)線程。

void serialThread::setupPort(const QString &portName, int waitTimeout, const QByteArray &msg){
    qDebug() << "Send Message " << msg.toHex();
    QMutexLocker locker(&mutex);
    this->portName = portName;
    this->waitTimeout = waitTimeout;
    this->msg = msg;
    if(!isRunning())
        start();
    else
        cond.wakeOne();
}

Run功能 - 處理發送和接收

void serialThread::run(){
    bool currentPortNameChanged = false;
    qDebug() << "Thread executed";
    mutex.lock();
    QString currentPortName;
    if(currentPortName != portName){
        currentPortName = portName;
        currentPortNameChanged = true;
    }

    int currentWaitTimeout = waitTimeout;
    QByteArray sendMsg = msg;
    mutex.unlock();
    QSerialPort serial;

    while(!quit){
        if(currentPortNameChanged){
            serial.close();
            serial.setPortName("COM3");

            if (!serial.open(QIODevice::ReadWrite)) {
                emit error(tr("Can't open %1, error code %2")
                           .arg(portName).arg(serial.error()));
                return;
            }

            if (!serial.setBaudRate(QSerialPort::Baud19200)) {
                emit error(tr("Can't set baud rate 9600 baud to port %1, error code %2")
                           .arg(portName).arg(serial.error()));
                return;
            }

            if (!serial.setDataBits(QSerialPort::Data8)) {
                emit error(tr("Can't set 8 data bits to port %1, error code %2")
                           .arg(portName).arg(serial.error()));
                return;
            }

            if (!serial.setParity(QSerialPort::NoParity)) {
                emit error(tr("Can't set no patity to port %1, error code %2")
                           .arg(portName).arg(serial.error()));
                return;
            }

            if (!serial.setStopBits(QSerialPort::OneStop)) {
                emit error(tr("Can't set 1 stop bit to port %1, error code %2")
                           .arg(portName).arg(serial.error()));
                return;
            }

            if (!serial.setFlowControl(QSerialPort::NoFlowControl)) {
                emit error(tr("Can't set no flow control to port %1, error code %2")
                           .arg(portName).arg(serial.error()));
                return;
            }
        }

        //write request
        serial.write(msg);
        if (serial.waitForBytesWritten(waitTimeout)) {
            //! [8] //! [10]
            // read response
            if (serial.waitForReadyRead(currentWaitTimeout)) {
                QByteArray responseData = serial.readAll();
                while (serial.waitForReadyRead(10)){
                    responseData += serial.readAll();
                }

                QByteArray response = responseData;
                //! [12]
                emit this->sendResponse(response);
                //! [10] //! [11] //! [12]
            } else {
                emit this->timeout(tr("Wait read response timeout %1")
                             .arg(QTime::currentTime().toString()));
            }
            //! [9] //! [11]
        } else {
            emit timeout(tr("Wait write request timeout %1")
                         .arg(QTime::currentTime().toString()));
        }
        mutex.lock();
        cond.wait(&mutex);
        if (currentPortName != portName) {
            currentPortName = portName;
            currentPortNameChanged = true;
        } else {
            currentPortNameChanged = false;
        }
        currentWaitTimeout = waitTimeout;
        sendMsg = msg;
        mutex.unlock();
    }
    serial.close();
}

handleResponse函數,SLOT接收響應信號

void Device::handleResponse(const QByteArray &msg){
    qDebug() << "Read: " << msg.toHex();
}

我收到以下輸出:

Has device  "Living Room Light" Changed? 
Send Message  "02621de94b051900" 
Has device  "Bedroom Light" Changed? 
Send Message  "026220cbcf051900" 
Thread executed 
Read:  "026220cbcf05190006" 
Polling for changes... 
Has device  "Living Room Light" Changed? 
Send Message  "02621de94b051900" 
Has device  "Bedroom Light" Changed? 
Send Message  "026220cbcf051900" 
Read:  "025020cbcf1edaf721000002621de94b05190006" 
Polling for changes... 
Has device  "Living Room Light" Changed? 
Send Message  "02621de94b051900" 
Has device  "Bedroom Light" Changed? 
Send Message  "026220cbcf051900" 
Read:  "02501de94b1edaf72100ff02621de94b05190006" 

這里有兩個問題。

  1. 我從未收到有關第二個設備(卧室燈)的任何回復,這是第二個發送的消息。 似乎發送被阻止,你會如何建議我格式化我的發送,以便在收到第一次發送的響應后發送? 只有1個COM端口可用於發送/接收。 我相信我應該發送消息到設備1,接收設備1響應,發送到設備2,接收設備2.我最終會看到很多設備和使用等待條件的巨大交通堵塞,即。 在執行設備2的通信過程之前,等待設備1通信過程完成?

  2. 第一次讀取包含適當的前半部分接收。 Read: "026220cbcf05190006"第二次接收包含第一次響應的后半部分,然后是第二次響應的上半部分:讀取2 - Read: "025020cbcf1edaf721000002621de94b05190006"相應的完整響應是02621DE94B05190006 025020CBCF1EDAF72100FF (注意20CBCF是設備2的ID完整的回復示例)

我應該對從串口接收數據的方式進行哪些更正? 謝謝!

  1. 請參閱存儲庫中的BlockingMaster示例,並閱讀有關阻止I / O的文檔。 另外,不要不必要地使用阻塞I / O.

  2. 使用bytesAvailable()來獲取可用於讀取的數據的數量,因為不是您立即收到完整的響應包的事實。

我相信我的問題已經從這個問題的范圍轉移了。 在Kuzulis的幫助下,我實現了寫/讀功能,可以一致地成功發送和讀取串行消息。 Kuzulis建議使用同步阻塞通信模式,但后來決定異步非阻塞方法最適合我的應用程序。

我的實現緊跟在QSerialPort源文件提供的“Master”示例之后。

我使用CurrentStatus迭代Device對象的QList。 對於設備列表中的每個Light,我格式化一個8字節消息以查詢設備的當前狀態(ON / OFF)。

void Device::currentStatus(QList<Device *> * deviceList){
    QString devID, updateQry;
    int devStatus, updateStatus;
    updateStatus=0;
    QSqlQuery query;
    for(int i=0; i<deviceList->size(); i++){
        if(deviceList->at(i)->type == "Light"){
            devStatus = deviceList->at(i)->status;
            devID = deviceList->at(i)->deviceID;
            QByteArray msg;
            msg.resize(8);

            msg[0] = 0x02;
            msg[1] = 0x62;
            msg[2] = 0x00;
            msg[3] = 0x00;
            msg[4] = 0x00;
            msg[5] = 0x05;
            msg[6] = 0x19;
            msg[7] = 0x00;
            msg.replace(2, 3, QByteArray::fromHex( devID.toLocal8Bit() ) );
            qDebug() << "Has device " << deviceList->at(i)->name << "Changed?";

            emit writeRequest(msg);

            if(devStatus!=updateStatus){
                qDebug() << deviceList->at(i)->name << " is now: " << updateStatus;
                updateStatus = !updateStatus;
            }
        }
    }
}

在Device類構造函數中,我連接信號和插槽:

Device::Device(){

    serialTimer.setSingleShot(true);
    QObject::connect(&serial, SIGNAL(readyRead()),
                     this, SLOT(handleResponse()));
    QObject::connect(&serialTimer, SIGNAL(timeout()),
                     this, SLOT(processTimeout()));
    QObject::connect(this, SIGNAL(writeRequest(QByteArray)),
                     this, SLOT(writeSerial(QByteArray)));
}

currentStatus中發送消息后, emit writeRequest(msg); 叫做。 這將調度連接到插槽writeRequest的信號。 writeRequest用於設置並實際將消息寫入串行端口。

void Device::writeSerial(const QByteArray &msg){
    if (serial.portName() != "COM3") {
        serial.close();
        serial.setPortName("COM3");

        if (!serial.open(QIODevice::ReadWrite)) {
            processError(tr("Can't open %1, error code %2")
                         .arg(serial.portName()).arg(serial.error()));
            return;
        }

        if (!serial.setBaudRate(QSerialPort::Baud19200)) {
            processError(tr("Can't set rate 19200 baud to port %1, error code %2")
                         .arg(serial.portName()).arg(serial.error()));
            return;
        }

        if (!serial.setDataBits(QSerialPort::Data8)) {
            processError(tr("Can't set 8 data bits to port %1, error code %2")
                         .arg(serial.portName()).arg(serial.error()));
            return;
        }

        if (!serial.setParity(QSerialPort::NoParity)) {
            processError(tr("Can't set no patity to port %1, error code %2")
                         .arg(serial.portName()).arg(serial.error()));
            return;
        }

        if (!serial.setStopBits(QSerialPort::OneStop)) {
            processError(tr("Can't set 1 stop bit to port %1, error code %2")
                         .arg(serial.portName()).arg(serial.error()));
            return;
        }

        if (!serial.setFlowControl(QSerialPort::NoFlowControl)) {
            processError(tr("Can't set no flow control to port %1, error code %2")
                         .arg(serial.portName()).arg(serial.error()));
            return;
        }
    }
    qDebug() << "Message written";
    this->msgRequest = msg;
    serial.write(msgRequest);
    serialTimer.start(400);
}

設置串口后,我將當前消息保存到msgRequest 如果出現錯誤,可能必須使用此選項重新發送消息。 調用serial.write()后,我設置了400ms的計時器。 一旦這個計時器到期,我檢查從串口讀取的內容。

handleResponse()是每次QSerialPort發出readyRead()信號時調用的一個槽。 readyRead()將任何可用數據附加到QByteArray response

void Device::handleResponse(){
    response.append(serial.readAll());  
}

400ms后,serialTimer(單次定時器)將發出timeout()信號。 在將請求的消息寫入串行端口后serialTimer啟動serialTimer processTimeout()是我們在發送消息后最終檢查從PowerLinc調制解調器收到的響應的地方。 當消息發送到INSTEON PowerLinc調制解調器(PLM)時,PLM回送消息並附加0x06(正ACK)或0x15(NACK)。 processTimeout()我檢查以確保收到的最后一個字節是ACK字節,如果不是 - 重新發送我們最初請求的消息。

void Device::processTimeout(){
    qDebug() << "Read: " << response.toHex();
    int msgLength = this->msgRequest.length();
    if(response.at(msgLength)!=0x06){
        qDebug() << "Error, resend.";
        emit writeRequest(msgRequest);
    }
    response.clear();
}

我使用串行端口監視器4.0(Eltima軟件)來驗證串行端口上的寫入和讀取事務。 在下面,您可以看到1個樣本事務的日志打印輸出。

20:44:30:666 STATUS_SUCCESS 02 62 1d e9 4b 05 19 00   <--- Send
20:44:30:669 STATUS_SUCCESS 02 62 1d e9 4b 05 19 00 06   <--- Receive
20:44:30:875 STATUS_SUCCESS 02  <--- Receive
20:44:30:881 STATUS_SUCCESS 50 1d e9 4b 1e da f7 21 00 ff   <--- Receive

對於20次發送,我收到了相同的回復。 因此,我可以有把握地說我的數據到達不一致的問題已經解決了。 現在我正在努力處理多個寫請求,但我認為這是一個需要調查的單獨問題。 我感謝大家的支持。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM