PDA

View Full Version : QtSerialPort - SerialPortInfo returns 0 ports available



slanina
25th April 2013, 19:20
I have Qt 5.0.1 on Windows 7 64-bit and I have downloaded and installed QtSerialPort following instructions on Wiki. I use MS VC++ Express 2010 compiler.

With simple example code from cenumerator I get 0 ports available. However, in Control Panel and using terminal program I can connect to my serial device.
I also did a quick Python script and it works from there using PySerial.

Any thoughts how to fix this?



QList<QSerialPortInfo> serialPortInfoList = QSerialPortInfo::availablePorts();
QTextStream out(stdout);

out << QObject::tr("Total number of ports available: ") << serialPortInfoList.count() << endl;

foreach (const QSerialPortInfo &serialPortInfo, serialPortInfoList) {
out << endl
<< QObject::tr("Port: ") << serialPortInfo.portName() << endl
<< QObject::tr("Location: ") << serialPortInfo.systemLocation() << endl
<< QObject::tr("Description: ") << serialPortInfo.description() << endl
<< QObject::tr("Manufacturer: ") << serialPortInfo.manufacturer() << endl
<< QObject::tr("Vendor Identifier: ") << (serialPortInfo.hasVendorIdentifier() ? QByteArray::number(serialPortInfo.vendorIdentifier (), 16) : QByteArray()) << endl
<< QObject::tr("Product Identifier: ") << (serialPortInfo.hasProductIdentifier() ? QByteArray::number(serialPortInfo.productIdentifie r(), 16) : QByteArray()) << endl
<< QObject::tr("Busy: ") << (serialPortInfo.isBusy() ? QObject::tr("Yes") : QObject::tr("No")) << endl;
}





QT += core gui sql serialport


Output:

Total number of ports available: 0

ChrisW67
25th April 2013, 23:24
This may be an issue with Windows Setup API not showing the errant device.

What happens if you just try to open COMx?

kuzulis
26th April 2013, 08:27
2 slanina,

Possibly it is regression which belongs to a recent patch which had to optimize enumeration: https://codereview.qt-project.org/#change,53725

Also already created issue about this bug: https://bugreports.qt-project.org/browse/QTPLAYGROUND-27

May you give detailed information on the used device (VID/PID/Class GUID and etc), and also give *.inf driver files?


UPD:
At present the solution isn't known. Temporarily you can use workaround - simply to roll away change to this patch for QSerialPortInfo.

PS:
Win32 API again and again throws to us "surprises". :(
The behavior of Setup API doesn't give in to logic, or I don't understand something...

slanina
26th April 2013, 13:36
2 slanina,

Possibly it is regression which belongs to a recent patch which had to optimize enumeration: https://codereview.qt-project.org/#change,53725

Also already created issue about this bug: https://bugreports.qt-project.org/browse/QTPLAYGROUND-27

May you give detailed information on the used device (VID/PID/Class GUID and etc), and also give *.inf driver files?


UPD:
At present the solution isn't known. Temporarily you can use workaround - simply to roll away change to this patch for QSerialPortInfo.

PS:
Win32 API again and again throws to us "surprises". :(
The behavior of Setup API doesn't give in to logic, or I don't understand something...

It is a custom device I am developing from one sample given by NXP for their LPC series of microcontrollers (LPC11U35). It is based on the code that works fine on the device side as given by NXP. Actually based on that code I did a device and one code in October 2012 but then I have used QtExtSerialPort and it enumerated fine.
Enumeration would be beneficial to me, since I could then detect and parse manufacturer name and device name (basically me) and make sure the user does not need to select a COM port - so I could have an "auto connect" upon application loading.

Any hints how to rollback the patch? I have never done this on Windows.

Upload of .inf file seems to fail so I am copying it below. It uses NXP VID/PID since it is their example...



;************************************************* ***********
; Windows USB CDC ACM Setup File
; Copyright (c) 2000 Microsoft Corporation


[Version]
Signature="$Windows NT$"
Class=Ports
ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
Provider=%MFGNAME%
LayoutFile=layout.inf
CatalogFile=%MFGFILENAME%.cat
DriverVer=11/15/2007,5.1.2600.0

[Manufacturer]
%MFGNAME%=DeviceList, NTamd64

[DestinationDirs]
DefaultDestDir=12


;------------------------------------------------------------------------------
; Windows 2000/XP/Vista-32bit Sections
;------------------------------------------------------------------------------

[DriverInstall.nt]
include=mdmcpq.inf
CopyFiles=DriverCopyFiles.nt
AddReg=DriverInstall.nt.AddReg

[DriverCopyFiles.nt]
usbser.sys,,,0x20

[DriverInstall.nt.AddReg]
HKR,,DevLoader,,*ntkern
HKR,,NTMPDriver,,%DRIVERFILENAME%.sys
HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"

[DriverInstall.nt.Services]
AddService=usbser, 0x00000002, DriverService.nt

[DriverService.nt]
DisplayName=%SERVICE%
ServiceType=1
StartType=3
ErrorControl=1
ServiceBinary=%12%\%DRIVERFILENAME%.sys

;------------------------------------------------------------------------------
; Vista-64bit Sections
;------------------------------------------------------------------------------

[DriverInstall.NTamd64]
include=mdmcpq.inf
CopyFiles=DriverCopyFiles.NTamd64
AddReg=DriverInstall.NTamd64.AddReg

[DriverCopyFiles.NTamd64]
%DRIVERFILENAME%.sys,,,0x20

[DriverInstall.NTamd64.AddReg]
HKR,,DevLoader,,*ntkern
HKR,,NTMPDriver,,%DRIVERFILENAME%.sys
HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"

[DriverInstall.NTamd64.Services]
AddService=usbser, 0x00000002, DriverService.NTamd64

[DriverService.NTamd64]
DisplayName=%SERVICE%
ServiceType=1
StartType=3
ErrorControl=1
ServiceBinary=%12%\%DRIVERFILENAME%.sys


;------------------------------------------------------------------------------
; Vendor and Product ID Definitions
;------------------------------------------------------------------------------
; When developing your USB device, the VID and PID used in the PC side
; application program and the firmware on the microcontroller must match.
; Modify the below line to use your VID and PID. Use the format as shown below.
; Note: One INF file can be used for multiple devices with different VID and PIDs.
; For each supported device, append ",USB\VID_xxxx&PID_yyyy" to the end of the line.
;------------------------------------------------------------------------------
[SourceDisksFiles]
[SourceDisksNames]
[DeviceList]
%DESCRIPTION%=DriverInstall, USB\VID_1FC9&PID_2047

[DeviceList.NTamd64]
%DESCRIPTION%=DriverInstall, USB\VID_1FC9&PID_2047


;------------------------------------------------------------------------------
; String Definitions
;------------------------------------------------------------------------------
;Modify these strings to customize your device
;------------------------------------------------------------------------------
[Strings]
MFGFILENAME="CDC_vista"
DRIVERFILENAME ="usbser"
MFGNAME="http://www.lpcware.com/content/project/LPCUSBlib"
INSTDISK="LPCUSBlib CDC Driver Installer"
DESCRIPTION="Communications Port"
SERVICE="USB RS-232 Emulation Driver"

kuzulis
26th April 2013, 14:23
It is clear. It seems that this problem is observed with USB CDC ACM devices.

Please try new patch: https://bugreports.qt-project.org/browse/QTPLAYGROUND-27

And please let me know about results.

kuzulis
27th April 2013, 11:48
Guys, seems that I'm found some solution. Please waiting...

Added after 1 37 minutes:

The patch is updated: https://codereview.qt-project.org/#change,54832

Please check its functionality, HowTo do it see: https://bugreports.qt-project.org/browse/QTPLAYGROUND-27

slanina
28th April 2013, 17:10
I have applied the patch and now I can enumerate my device. I can connect and read/write data to it.

BUT :)

My device receives *any single character* and sends the following output:

DAT:9224,45080,8,36,0,24,176,0\n\r


It seems after some time I get constant reading problems. Below is my code of the QThread run() method:



void DataFeeder::run()
{
qDebug() << "DataFeeder - Started.";

done = false;
bool retval = true;

QSerialPort serial;
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::Baud9600)) {
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.";

mutex->lock();

if(retval)
{
do
{

QByteArray requestData = QString("d").toLocal8Bit();
serial.write(requestData);
if(serial.waitForBytesWritten(5000))
{
if(serial.waitForReadyRead(2000))
{
QByteArray responseData = serial.readAll();
QString data;
data = data.fromLatin1(responseData).trimmed();
qDebug() << "Got:$" << data << "$";

}
else
{
emit feederError("Data not correct - list size.");
}
}
else
{
emit feederError("WaitForReadyRead failed.");
}
}
else
{
emit feederError("WaitForBytesWritten failed.");
}

} while( !waitCondition->wait(mutex, sleepTime) && !done);

}

mutex->unlock();

qDebug() << "DataFeeder - Ended.";

if(serial.isOpen())
{
serial.close();
}

}


Output is:

Trying to open "COM6"
"COM6" opened.
Got:$ "" $
Got:$ "DAT:10760,43288,8,42,0,24,169,0" $
.
.
.
"WaitForReadyRead failed."
"WaitForReadyRead failed."
"WaitForReadyRead failed."
.
.
.


So first thing is that first write to the device always fails and I get back empty string.
And then after some time (approx after 10-15 writes of a single charachter and reads of data shown) I get that else {} block with timeout for read.

I have tried using terminal program and I wrote many charachters and got back data all the time with no blockage and failure.

Also I have noticed that readLine() fails and I get timeout so I use readAll() as in the blockingmaster example. However, from the device side I do send \r\n characters at the end.

Also if I keep waitForBytesWritten timeout at 1000 msec, then I get write timeouts even though I am sending only 1 charachter at 9600 bps. Read timeout of 2000msec should be enough for reading this small dataset.

kuzulis
28th April 2013, 19:33
That I can advise in this situation:

1. Add a method of obtaining the error code after unsuccessful waitForReadyRead() and output the error code to the console.
2. Make the same project only with use of asynchronous approach (without waitForXXX() methods), see Master example. Because implementation of the synchronous approach isn't up to the end designed and it isn't recommended.

slanina
1st May 2013, 07:00
That I can advise in this situation:

1. Add a method of obtaining the error code after unsuccessful waitForReadyRead() and output the error code to the console.
2. Make the same project only with use of asynchronous approach (without waitForXXX() methods), see Master example. Because implementation of the synchronous approach isn't up to the end designed and it isn't recommended.

I have made a new code based on the Master example. And I am experiencing the same problem....after some time it starts to get errors. Below is error message which I have printed out:


Timeout waiting to read serial port "COM3"
Error code: 9 String: "Unknown error"




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


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::Baud9600)) {
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;
qDebug() << "Error code: " << serial.error() << " String: " << serial.errorString();
}

void DataFeeder2::acquireData()
{
if(!waitingForReply)
{
serial.write(QString("d").toLocal8Bit());
errorTimer.start(2000);
waitingForReply = true;
}
}

void DataFeeder2::readResponse()
{
errorTimer.stop();

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

waitingForReply = false;

}


In readResponse() I have tried readAll() to read everything and readLine() which I would prefer since it is blocking and my client terminates the string with \n\r. In any case behaviour is the same - works fine and then after 1-2 minutes it stops.

When from GUI I call stop() method on my object and then start() again, it again successfully opens COM3, connects and starts reading data. Then again after some time (1-2 minutes) it stops.

kuzulis
1st May 2013, 09:29
For this question you needed to make a separate thread.

Concerning realization:

Not necessarily that readAll() (and furthermore readLine()) will return full string. At first it is necessary for you it will be convinced that in the buffer really there is a full answer.

For your case you have to check canReadLine() (or bytesAvailable()). If it returns false, it means that full string yet isn't in the buffer and you have to wait the rest data at the following readyRead().

The signal readyRead() is emits at reception at least one symbol. Therefore readAll () has to return at least one symbol.

So, you need wait until full string is not received and then read it.