Qt Serial Port - Read Data Continuously

I send (write) bytes to the device through my serial port. I use the QSerialPort module ( http://qt-project.org/wiki/QtSerialPort to create IO device support. When I send messages to the INSTEON modem (serial), after reading my message, the device sends back a copy of my message + 0x06 (ACK Byte ) followed by a status message.

I tested my post with DockLight ( http://www.docklight.de/ ). I am sending the following message to request device status:

02 62 1D E9 4B 05 19 00 

Using Docklight, I get the answer:

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

The returned message indicates exactly what I expect the device is on. If you are turned off, the modem will send back 0x00 to the last byte position if the device is turned off. Now my problem is I do not have to properly configure my function to send, and then receive response bytes. I have tried many different examples and configurations, currently I am using the following:

Signal Connector Setup:

 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))); 

Function used to iterate through QList devices. If the required device type ("Light"), we will format the device identifier in the intended structure of the QByteArray message. Pass the message to the stream for sending. (Theme is modified from the QSerialPort BlockingMaster example.

 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 function used to set local thread variables and execute a thread.

 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 Function - Handling Sent and Received

 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 that receives a response

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

I get the following output:

 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" 

Two questions here.

  • I never get any response regarding the second device (Light Bedroom), this is a message that is sent by the second. It seems that the sending is blocked, how would you recommend me to format my sending in order to send a message after receiving a response for the first sending? There is only 1 COM port that you can use to send / receive. I believe that I should send a message to device 1, receive a response from device 1, send to device 2, receive device 2. Can I eventually see a huge traffic jam with a large number of devices and use the waiting conditions, i.e. wait for the completion of the communication process of device 1 before completing the comm process for device 2?

  • The very first reading contains the corresponding first half of the reception. Read: "026220cbcf05190006" second trick contains the second half of the first answer, followed by the first half of the second answer: Read 2 - Read: "025020cbcf1edaf721000002621de94b05190006" corresponding complete answer is 02621DE94B05190006 025020CBCF1EDAF72100FF (device identifier 20CBCF )

What corrections should be made to the method of receiving data from the serial port? Thanks!

+6
source share
2 answers

I believe that my problems have moved from the scope of this issue. With Kuzulis, I implemented Write / Read functions to successfully send and read sequential messages. Kuzulis recommended using a synchronous lock communication pattern, but later it was decided that the asynchronous non-locking method works best for my application.

My implementation closely follows the Wizard example provided by the QSerialPort source files.

I am using CurrentStatus to iterate through QList Device objects. For each β€œLight” list in the β€œDevice” list, I will format an 8-byte message to request the current status of the device (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; } } } } 

In the constructor of the Device class, I connect the signals and slots:

 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))); } 

After the message for sending CurrentStatus been prepared, emit writeRequest(msg); is called emit writeRequest(msg); . This sends a signal that is connected to the writeRequest slot. writeRequest is used to set up and actually write a message to the serial port.

 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); } 

After setting up the serial port, I save the current message until msgRequest . This may be required to resend the message if there is an error. After calling serial.write() , I set the timer to 400 ms. After this timer expires, I check what has been read from the serial port.

handleResponse() is a slot that is called every time a QSerialPort emits a readyRead() signal. readyRead() adds any available data to the QByteArray response .

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

After 400 ms, serialTimer (one shot timer) issues a timeout() signal. serialTimer was launched immediately after entering our requested message into the serial port. processTimeout() is where we finally check the response received from the PowerLinc bridge after sending our message. When messages are sent to the INSTEON PowerLinc (PLM) modem, PLM sends the message back and adds either 0x06 (positive ACK) or 0x15 (NACK). In processTimeout() I check that the last byte is an ACK byte, if not, repeat our originally requested message.

 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(); } 

I used Serial Port Monitor 4.0 (Eltima Software) to check write and read transactions on the serial port. Below you can view the log print for 1 sample transaction.

 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 

For 20 shipments, I received the same answer. Thus, I can say with confidence that my problems with inconsistent data arrival have been resolved. Now I'm struggling with a few recording requests, but I think this is a separate issue for research. I thank everyone for their support.

+3
source
  • See the BlockingMaster example in the repository and read the documentation on blocking I / O. Also, do not use blocking I / O unnecessarily.

  • Use bytesAvailable () to get the amount of data to read, because it is not a fact that you will immediately receive a complete response package.

+3
source

All Articles