PDA

View Full Version : QtSerialPort - reading fails after random times.



slanina
3rd May 2013, 17:43
As a follow up to the thread I posted about SerialPortInfo returning 0 ports available:
http://www.qtcentre.org/threads/54329-QtSerialPort-SerialPortInfo-returns-0-ports-available

I have made a code that uses master example (I also did blocking master which I was told should not be the approach right now since implementation is not yet ready).

I also did a suggestion that in readyRead() wait until all data is available and then process it and/or append the data to some buffer as it comes since readyRead is emited even 1 charachter is received out of N that could come.

My observation is that again after some random amount of time, reading simply stalls and timeout occurs.

I am sending 30 charachters so that would be about 270 bits. At 9600 bps this transfer would take 28 msec and at 57600 bps it would take about 4.7 msec. I have tested both speeds - same behavior.

My device does not constantly stream data - a character (any) needs to be send and then only device sends a single line terminated with \n\r

I have put qDebug() statements when I am sending and receiving and when timeout occurs and following happens:

There is only one readyRead() signal being emitted and my slot is being called.
Inside the slot I am checking if data can be read with canReadLine() and then reading it with readAll() or readLine() - either works in my case.

At random times timeout occurs and at that point readyRead() seems never to be transmitted and readResponse() was never called. bytesAvailable() call returns 0 in qDebug() from timeout handler which is strange.

I did a quick "fix" by closing and opening the serial port in timeout slot. Then code works again until next time reading fails - it may happen immediately or after some random time and random amount of successful reads.

I have tested my device using Terminal program and also using Python PySerial library and it works fine by reading for several hours (overnight)

.inf file for driver is available at my initial thread.

Below is the new code:




DataFeeder2::DataFeeder2(QString useComPort, QObject *parent, int nSleepTime) :
QObject(parent), sleepTime(nSleepTime), comPort(useComPort), waitingForReply(false)
{

QSettings settings;
//sleepTime = settings.value("Interval", QVariant(nSleepTime)).toInt()*60*1000; // in msec
sleepTime = 1000; // DEBUG only

serial.setPortName(comPort);
timer.setInterval(sleepTime);
errorTimer.setSingleShot(true);

connect(&serial, SIGNAL(readyRead()),
this, SLOT(readResponse()));
connect(&timer, SIGNAL(timeout()),
this, SLOT(acquireData()));
connect(&errorTimer, SIGNAL(timeout()),
this, SLOT(processTimeout()));
}

DataFeeder2::~DataFeeder2()
{
if(serial.isOpen())
{
serial.close();
}
}

bool DataFeeder2::start()
{
bool retval = true;

serial.setPortName(comPort);

qDebug() << "Trying to open " << comPort;
if (!serial.open(QIODevice::ReadWrite)) {
qDebug() << QString("Can't open %1, error code %2").arg(comPort).arg(serial.error());
retval = false;
}

if (!serial.setBaudRate(QSerialPort::Baud57600)) {
qDebug() << QString("Can't set baud rate 9600 baud to port %1, error code %2").arg(comPort).arg(serial.error());
retval = false;
}

if (!serial.setDataBits(QSerialPort::Data8)) {
qDebug() << QString("Can't set 8 data bits to port %1, error code %2").arg(comPort).arg(serial.error());
retval = false;
}

if (!serial.setParity(QSerialPort::NoParity)) {
qDebug() << QString("Can't set no patity to port %1, error code %2").arg(comPort).arg(serial.error());
retval = false;
}

if (!serial.setStopBits(QSerialPort::OneStop)) {
qDebug() << QString("Can't set 1 stop bit to port %1, error code %2").arg(comPort).arg(serial.error());
retval = false;
}

if (!serial.setFlowControl(QSerialPort::NoFlowControl )) {
qDebug() << QString("Can't set no flow control to port %1, error code %2").arg(comPort).arg(serial.error());
retval = false;
}


qDebug() << comPort << " opened.";


waitingForReply = false;

timer.start();


return retval;

}

void DataFeeder2::stop()
{
timer.stop();
errorTimer.stop();
serial.close();

qDebug() << "DataFeeder2 stopped.";
}

void DataFeeder2::setSleepTime(int nSleepTime)
{
if(sleepTime != nSleepTime)
{
sleepTime = nSleepTime;
timer.setInterval(sleepTime);
emit sleepTimeChanged(sleepTime);
}
}

void DataFeeder2::processTimeout()
{
qDebug() << "Timeout waiting to read serial port " << comPort << " - " << QDateTime::currentDateTimeUtc().time().toString();
qDebug() << "Error code: " << serial.error() << " String: " << serial.errorString();
qDebug() << "Bytes avail: " << serial.bytesAvailable();
emit feederError(QString("<font color=\"#ff0000\">ERROR(%1)</font>").arg(serial.error()));

// DEBUG only
stop();
start();
// END DEBUG only
}

void DataFeeder2::acquireData()
{
if(!waitingForReply)
{
serial.write(QString("d").toLocal8Bit());
errorTimer.start(4000);
waitingForReply = true;
qDebug() << "Sent at: " << QDateTime::currentDateTimeUtc().time().toString();
}
}

void DataFeeder2::readResponse()
{
qDebug() << "In ReadResponse()" << QDateTime::currentDateTimeUtc().time().toString();
if(serial.canReadLine())
//if(serial.bytesAvailable()>10)
{
errorTimer.stop();



responseData = serial.readAll();
//responseData = serial.readLine();
QString data;
data = data.fromLatin1(responseData).trimmed();
qDebug() << "Got:$" << data << "$";


waitingForReply = false;
}

}

kuzulis
3rd May 2013, 18:49
Please provide the minimum source project which reproduces a problem. I will try it to build and to test.

Also show data string/array which you send and you expect for reception.

slanina
3rd May 2013, 20:31
Please provide the minimum source project which reproduces a problem. I will try it to build and to test.

Also show data string/array which you send and you expect for reception.


Here is the code:

9009

And here are the strings and code from the device firmware source code:


Got:$ "DAT:2067,6502,8,19,0,25,102,0" $
MainWindow::handleDataItem() - Time: QDateTime("Fri May 3 19:24:43 2013") "DAT:2067,6502,8,19,0,25,102,0"
Sent at: "19:24:44"
In ReadResponse() "19:24:44"
Got:$ "DAT:2066,6503,8,18,0,25,103,0" $
MainWindow::handleDataItem() - Time: QDateTime("Fri May 3 19:24:44 2013") "DAT:2066,6503,8,18,0,25,103,0"
Sent at: "19:24:45"
Timeout waiting to read serial port "COM3" - "19:24:49"
Error code: 9 String: "Unknown error"
Bytes avail: 0
Feeder Error: "<font color="#ff0000">ERROR(9)</font>"


Code from device:



snprintf((char *)buff, MAX_BUFF, "DAT:%d,%d,%d,%d,%d,%d,%d,%d\n\r", humi_val, temp_val, humiMsb, humiLsb, humiError, tMsb,tLsb,tError);
len = strlen((const char*)buff);
UsbdCdc_SendData(buff, len);

slanina
16th July 2013, 20:50
An update:

Device uses NXP LPC11U35 MCU which has on-board USB CDC stack implementation.

I have tested the same code from Qt on Linux (Ubuntu 12.04) with Qt 5.0 and new 5.1 - and data is being read correctly from the device all the time.

Still even when compling with Qt 5.1 on Windows 7 64-bit, after some time ERROR 9 appears - some time perhaps 1K transfer max.
However, on Windows C# or Python application works perfectly.

I guess QtSerialPort is broken on Windows still.