I have the following code:
QSerialPort arduPort("COM5");
arduPort.setBaudRate(QSerialPort::Baud9600);
arduPort.setDataBits(QSerialPort::Data8);
arduPort.setParity(QSerialPort::NoParity);
arduPort.setStopBits(QSerialPort::OneStop);
arduPort.setFlowControl(QSerialPort::NoFlowControl);
arduPort.open(QSerialPort::ReadWrite);
cout<<arduPort.isReadable()<<endl;
cout<<arduPort.isWritable()<<endl;
arduPort.write("a");
QByteArray s=arduPort.readAll();
cout<<QString(s).toStdString()<<endl;
And the next code in Arduino:
int inByte = 0;
void setup()
{
Serial.begin(9600);
while(!Serial){;}
int i=0;
}
void loop()
{
if(Serial.read()=='a')
Serial.write('b');
}
First I send an 'a' to the Arduino, and the ARduino must respond with 'b'. But when I read the port of the Arduino, I recieve '' only.
Anyone knows why I recieve '' instead of 'b'? Thanks for your time.
Update: See bottom of this answer for the answer. TL;DR: You have so set the baud rate (and presumably all the other settings) after you open the port.
I believe this is a bug in the Windows implementation of QSerialPort. I haven't been able to narrow down the cause yet but I have the following symptoms:
Load the Arduino (Uno in my case; Leonardo may behave very differently) with the ASCII demo. Unplug and replug the Arduino. Note that the TX light doesn't come on.
Connect to it with Putty or the Arduino serial port monitor. This resets the Arduino and then prints the ASCII table. The TX light is on continuously as expected.
Unplug/replug the Arduino and this time connect to it with a QSerialPort program. This time despite the port being opened ok the TX light never comes on and readyRead()
is never triggered. Also note that the Arduino is not reset because by default QSerialPort does not change DTR. If you do QSerialPort::setDataTerminalReady(false);
then pause for 10ms then set it true
it will reset the Arduino as expected but it still doesn't transmit.
Note that if you have an Arduino program that transmits data continuously (ASCII example stops), if you open the port with putty so that it starts transmitting and then open it with QSerialPort without unplugging the cable it will work! However as soon as you unplug/plug the cable it stops working again.
This makes me suspect that putty is setting some serial port option that is required by the arduino and reset when you replug the cable. QSerialPort obviously doesn't change this value.
Here are the settings used by Putty as far as I can tell:
dcb.fBinary = TRUE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fDsrSensitivity = FALSE;
dcb.fTXContinueOnXoff = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fAbortOnError = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.BaudRate = ...;
dcb.ByteSize = ...;
And by QSerialPort:
dcb.fBinary = TRUE;
dcb.fDtrControl = unchanged!
dcb.fDsrSensitivity = unchanged!
dcb.fTXContinueOnXoff = unchanged!
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fAbortOnError = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = unchanged!
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.BaudRate = ...;
dcb.ByteSize = ...;
So I think it must be one of those unchanged values which makes the Arduino think that it isn't connected. From the DCB documentation I suspect fTxContinueOnXoff
.
Ok I am going to write a little program to read these settings and see what changes.
Update 1
Ok I wrote my program and made the following discovery. The differences after running putty and just my Qt program were:
- BaudRate: THIS WASN'T SET BY QT!!!!!!! It turns out you can only set the baud rate after you open the port.. Otherwise it is left at the previous value which is 0 when you first plug in the cable.
- fDtrControl: Set to 1 by Putty, left at 0 by Qt.
- fOutX and fInX: Both also set to 1 by Putty and left at 0 by Qt.
After moving all my set...()
function calls after the open it worked perfectly. I didn't have to fiddle with DtrControl or Out/InX. (Although I have also set DTR high manually.)
Update 2
While setting all the parameters I thought it would be a good idea to set the error policy to 'skip'. DON'T DO THIS! LEAVE IT ON IGNORE! Otherwise it fucks everything up and adds weird delays to all your communications.
Setting the ports before open is not quite possible before Qt 5.2. The reason is that the original design was a bit too low-level for the class rather properly object oriented. I had been considered for a long-time whether to change it and in the end I actually decided to do so.
I have just submitted a change that is now under code review that will make your original concept work, too.
Here you can find the details:
Make it possible to set the port values before opening
The summary can be read here for the change:
Make it possible to set the port values before opening
This patch also changes the behavior of the open method. We do not use port
detection anymore for good. It has been a broken concept, and it is very
unlikely that anyone has ever relied on it. If anyone had done that, they would
be in trouble anyway, getting noisy warnings needlessly and all that.
The other option was also considered to keep this behavior optionally as the
default, but that would complicate the API too much without not much gain.
The default port settings are now also the sane 9600,8,N,1, and no flow control.
Also please note that the serial port is closed automatically in the open method
if any of the settings fails.
Please update your Qt version (at least to Qt 5.2) or you can backport the change yourself. Then, it is possible to write this code and actually it is even recommended:
QSerialPort arduPort("COM5");
arduPort.setBaudRate(QSerialPort::Baud9600);
arduPort.setDataBits(QSerialPort::Data8);
arduPort.setParity(QSerialPort::NoParity);
arduPort.setStopBits(QSerialPort::OneStop);
arduPort.setFlowControl(QSerialPort::NoFlowControl);
arduPort.open(QSerialPort::ReadWrite);
BaudRate: THIS WASN'T SET BY QT!!!!!!! It turns out you can only set
the baud rate after you open the port.. Otherwise it is left at the
previous value which is 0 when you first plug in the cable.
Yes, it is true. But it has been fixed and will be available in Qt 5.3
fDtrControl: Set to 1 by Putty, left at 0 by Qt.
No. Qt do not touch the DTR signal at opening. This signal will be cleared only when was set to DTR_CONTROL_HANDSHAKE. Because QtSerialPort do not support the DTR/DSR flow control. So, in any case you can control the DTR by means of QSerialPort::setDataTerminalReady(bool).
PS: I mean current Qt 5.3 release
fOutX and fInX: Both also set to 1 by Putty and left at 0 by Qt.
This flag's are used only when you use QSerialPort::Software flow control (Xon/Xoff). But you are use the QSerialPort::NoFlowControl (as I can see from your code snippet), so, all right. So, please check that you use Putty with the "None" flow control too.
While setting all the parameters I thought it would be a good idea to
set the error policy to 'skip'.
Please use only QSerialPort::Ignore policy (by default). Because other policies is deprecated (all policies) and will be removed in future.
UPD:
dcb.fRtsControl = RTS_CONTROL_ENABLE;
Ahh, seems that your Arduino expect that RTS signal should be enabled by default. In this case you should to use QSerialPort::setRequestToSend(bool). But it is possible only in QSerialPort::NoFlowControl mode.
I.e. RTS always will be in RTS_CONTROL_DISABLE or RTS_CONTROL_HANDSHAKE after opening of port (depends on your FlowControl settings, QSerialPort::setFlowControl() ).