I am trying to write a QT application that can communicate with a propeller over USB. Reading through the documentation QSerialPort seems to be exactly what I need. For a simple test, I am trying to send the number "2" to my propeller project. The project itself has its own OLED screen that I can read results on. Here is the QT code itself:
this->Serial=new QSerialPort(this);
this->Serial->setPortName("/dev/ttyUSB0");
this->Serial->setBaudRate(QSerialPort::Baud9600);
connect(this->Serial,SIGNAL(error(QSerialPort::SerialPortError)),this,SLOT(errorReport(QSerialPort::SerialPortError)));
if(this->Serial->open(QIODevice::ReadWrite))
{
QByteArray dayArray;
QDataStream stream(&dayArray, QIODevice::WriteOnly);
stream << 2;
qDebug()<< dayArray.toHex();
qDebug()<< this->Serial->portName();
if(this->Serial->setDataBits(QSerialPort::Data8))
{
qDebug()<<"bits set to 8";
}
this->Serial->write(dayArray.toHex());
this->Serial->waitForBytesWritten(-1);
this->Serial->close();
}
and the error report slot is:
void serial::errorReport(QSerialPort::SerialPortError error)
{
if(error!=0)
qDebug()<<"ERROR:"<<endl<<error;
}
The console debug statements print this:
"00000002"
"ttyUSB0"
bits set to 8
However when I read the values the propeller recieves, it ranges from 0xD0, 0xF0, or 0xE0, not the 0x02 I was expecting. The baud rates in the code match that of the chip and I am really not sure what is wrong. Can anyone point out the fault?
EDIT:
I know that the propeller code isn't the problem as it works fine with the Arduino's IDE serial console.
I finally figured it out. The working code is as follows:
if(this->Serial->open(QIODevice::ReadWrite))
{
this->Serial->setBaudRate(QSerialPort::Baud9600);
this->Serial->setParity(QSerialPort::NoParity);
this->Serial->setStopBits(QSerialPort::OneStop);
this->Serial->setFlowControl(QSerialPort::NoFlowControl);
this->Serial->setDataBits(QSerialPort::Data8);
QByteArray dayArray;
dayArray[0]=2;
this->Serial->write(dayArray);
this->Serial->waitForBytesWritten(-1);
this->Serial->close();
}
Related
I'm writing a Qt GUI application that receives and sends data to an Arduino over serial. It writes correctly, but when trying to read it doesn't work.
My problem is:
I have an Arduino code that sends things over Serial, I programmed it to turn on and off a LED on pin 13 depending on the received data:
#define ARDUINO_TURNED_LED_ON "LEDON"
#define ARDUINO_TURNED_LED_OFF "LEDOFF"
#define PC_REQUESTED_LED_STATE_CHANGE u8"CHANGELED"
void setup()
{
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(13, OUTPUT);
}
String serialCmd = "";
bool ledState=false;
void loop()
{
// put your main code here, to run repeatedly:
if (Serial.available())
{
serialCmd=Serial.readString();
if (serialCmd==PC_REQUESTED_LED_STATE_CHANGE)
{
if (ledState)
{
digitalWrite(13, LOW);
Serial.write(ARDUINO_TURNED_LED_OFF);
}
else
{
digitalWrite(13, HIGH);
Serial.write(ARDUINO_TURNED_LED_ON);
};
ledState=!ledState;
serialCmd = "";
}
}
}
I'm using the following signal and slot at MainWindow class:
#define ARDUINO_TURNED_LED_ON "LEDON"
#define ARDUINO_TURNED_LED_OFF "LEDOFF"
#define PC_REQUESTED_LED_STATE_CHANGE u8"CHANGELED"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* Create Object the Class QSerialPort*/
serialPort = new QSerialPort(this);
/* Create Object the Class Arduino to manipulate read/write*/
arduino = new Arduino(serialPort);
//connect signal:
connect(serialPort, SIGNAL(readyRead()), this, SLOT(ReadData()));
}
//slot
void MainWindow::ReadData()
{
QString received = arduino->Read();
qDebug() << "received data:" << received;
if (received==ARDUINO_TURNED_LED_ON)
{
qDebug() << "led on";
}
else if (received==ARDUINO_TURNED_LED_OFF)
{
qDebug() << "led off";
}
}
And the Arduino::Read() method that is called by the previous slot:
QString Arduino::Read()
{
QString bufRxSerial;
qDebug() << "bytes available:" << serialPort->bytesAvailable();
/* Awaits read all the data before continuing */
while (serialPort->waitForReadyRead(20)) {
bufRxSerial += serialPort->readAll();
}
return bufRxSerial;
}
When writing to the Arduino it works well, the LED changes as it should, the problem happens when Qt tries to read the replied data.
In most of the times that the Arduino sends something, the signal is emitted and serialPort->bytesAvailable() returns a number that is not zero, but
serialPort->waitForReadyRead(20) times out without receiving anything and serialPort->readAll() returns an empty QString. If I use serialPort->waitForReadyRead(-1) the window freezes.
The weirdest thing is that, in a random moment that something is sent, everything works well and the function returns all the data that couldn't be read previously.
Here's an example of what happens:
1st attempt (fails)
Qt writes PC_REQUESTED_LED_STATE_CHANGE (u8"CHANGELED") to the serial port
Arduino LED changes its state (turns on)
Arduino writes ARDUINO_TURNED_LED_ON ("LEDON") to the serial port
Qt reads an empty QString from the Arduino
2nd attempt (fails)
Qt writes PC_REQUESTED_LED_STATE_CHANGE (u8"CHANGELED") to the serial port
Arduino LED changes its state (turns off)
Arduino writes ARDUINO_TURNED_LED_OFF ("LEDOFF") to the serial port
Qt reads an empty QString from the Arduino
3rd attempt (this is a random attempt that everything works)
Qt writes PC_REQUESTED_LED_STATE_CHANGE (u8"CHANGELED") to the serial port
Arduino LED changes its state (turns on)
Arduino writes ARDUINO_TURNED_LED_ON ("LEDON") to the serial port
Qt reads "LEDONLEDOFFLEDON" from the Arduino, that is everything that couldn't be read previosly.
4th attempt (fails)
Qt writes PC_REQUESTED_LED_STATE_CHANGE (u8"CHANGELED") to the serial port
Arduino LED changes its state (turns off)
Arduino writes ARDUINO_TURNED_LED_OFF ("LEDOFF") to the serial port
Qt reads an empty QString from the Arduino
5th attempt (this is another random attempt that everything works)
Qt writes PC_REQUESTED_LED_STATE_CHANGE (u8"CHANGELED") to the serial port
Arduino LED changes its state (turns on)
Arduino writes ARDUINO_TURNED_LED_ON ("LEDON") to the serial port
Qt reads "LEDOFFLEDON" from the Arduino, that is everything that couldn't be read previosly.
I tested using the Arduino IDE serial monitor and it worked as it was supposed to be: I wrote "CHANGELED", then the LED changed and the Arduino replied "LEDON" or "LEDOFF" all the times.
What can I do to solve this problem?
Thanks
Edit: the complete code can be found here
The Read method is incorrect and unnecessary. You should never use the waitFor methods. You also shouldn't be using QString when dealing with simple ASCII data that QByteArray handles fine:
class MainWindow : ... {
QByteArray m_inBuffer;
...
};
void MainWindow::ReadData() {
m_inBuffer.append(arduino->readAll());
QByteArray match;
while (true) {
if (m_inBuffer.startsWith((match = ARDUINO_TURNED_LED_ON))) {
qDebug() << "led on";
} else if (m_inBuffer.startsWith((match = ARDUINO_TURNED_LED_OFF))) {
qDebug() << "led off";
} else {
match = {};
break;
}
}
m_inBuffer.remove(0, match.size());
}
The deal is simple: ReadData can be called with any number of bytes available - even a single byte. Thus you must accumulate data until a full "packet" is received. In your case, packets can only be delineated by matching a full response string.
You'd make your life much easier if Arduino sent full lines - replace Serial.write with Serial.println. Then the lines are packets, and you don't need to buffer the data yourself:
void MainWindow::ReadData() {
while (arduino->canReadLine()) {
auto line = arduino->readLine();
line.chop(1); // remove the terminating '\n'
QDebug dbg; // keeps everything on a single line
dbg << "LINE=" << line;
if (line == ARDUINO_TURNED_LED_ON)
dbg << "led on";
else if (line == ARDUINO_TURNED_LED_OFF)
dbg << "led off";
}
}
See? It's much simpler that way.
You can also leverage Qt to "simulate" Arduino code without running real Arduino hardware, see this answer for an example.
This sounds like a buffering problem. Your Qt application receives the bytes, but there is nothing that tells it the reception is complete. I would add line feeds (\n) as terminator character after each string, just use Serial.println() instead of Serial.write(). Then you can use readLine() in Qt and always be sure that what you get is exactly one complete command string.
If you're on Linux you could also try to stty your device to raw with
stty -F /dev/ttyACM0 raw
to have the serial characters sent straight through instead of default line buffering (see What’s the difference between a “raw” and a “cooked” device driver?).
Note that then you might get a new issue: Your Qt application might be so fast that you receive only one letter at a time in your callback.
How can I write and read hex data when I open serial port?
if (serial->isOpen()) {
qDebug() << "seccus: " ;
static const char buff[] = {0xA1, 0xFF, 0xFF, 0x00,97};
serial->write(buff,sizeof(buff));
}
if (serial->bytesAvailable()) { // If there are bytes available
QByteArray f_data; // data container
f_data.clear();
if (serial->open(QIODevice::ReadWrite)) { // Try to open the port
while(serial->bytesAvailable()) { // Reading loop
f_data.append(serial->readAll());
}
serial->flush();
}
qDebug()<< "the result" << f_data; // Check the result
}
if(!serial->isOpen())
qDebug() << "carte disconnect"<<serial->errorString();
To debug your issue, I would start with simplifying what you're trying to do. Try writing some test code to start, to make sure things work as you expect:
Test serial is open - should not be
Open serial
Test serial is open - should be
Test if bytes are available - should not be
write data to serial. Flush.
Test if bytes are available - should be
Read and print to screen (i.e test your reading it correctly)
Close the serial port - test its not open
Perhaps if one of these steps doesn't work as you expect, you can at least focus on why that particular part is not working.
Good luck.
I've been trying to do a Lights GUI with an Arduino Mega 2560 with its Xbee Shield and two Xbee Pro S1, one connected to the Arduino and the other one to the PC. My problem is: however I can send data from Qt to my arduino and read it, i can't do the same in the other way. When trying to send a String as "Confirmado\r\n", it arrives to my Qt label wrong, sometimes I get the full String, other ones I receive half of it.
My arduino code is
char buffer[50];
String trama, dir, com, data;
int indexdir, indexcom, indexdata;
void setup(){
Serial.begin(9600);
}
void loop(){
trama= "Confirmado\r\n";
const char *bf = trama.c_str();
if(Serial.available() > 0)
{
Serial.readBytesUntil('/', buffer,500);
Serial.print(bf);
Serial.flush();
}
}
My Qt QSerialPort config is
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
serial = new QSerialPort(this);
serial->setPortName("COM3"); //COM-port your Arduino is connected to
serial->open(QIODevice::ReadWrite);
serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
connect(serial,SIGNAL(readyRead()),this,SLOT(serialReceived()));
}
And I send and read data like this
void MainWindow::serialReceived()
{
QByteArray readData = serial->readAll();
//while (serial->waitForReadyRead(500))
// readData.append(serial->readAll());
ui->label->setText(readData);
}
void MainWindow::writeData(const QByteArray &data)
{
serial->write(data);
serial->flush();
serial->waitForBytesWritten(500);
}
The toogle lines means I've tried both options...
I've noticed, doing Debug, that if I place a breakpoint in ui->label->setText(readData);; if it doesnt arrive well (the full "Confirmado\r\n" string), this breakpoint gets twice in this line, the first one readData equals the second half of the string (i.e "mado\r\n") and the other one it values the rest of the string (i.e "Confir").
I've also tried to set a higher baudrate, 57600, but I cant send or receive any data, though I've set the baudrate in the XCTU app before.
Does anyone know a way to receive the full string from Arduino? Or at leats how to setup properly Arduino's and PC's Xbee to work with higher baudrates?
Thanks for the answers, and sorry for my writing skills...
Try use serial->readLine() instead of serial->readall() you can for example wait in loop after the serial->canReadLine() returns the true then you be sure that the data are you received is a full string.
I'm trying to write \ read from a serial device using a usb / rs 232 cable.
I'm pretty sure that my code writes " #002s " (this is a control code) to the serial device, because
a) the serial port is open
b) the serial port is writable
c) the code successfully navigates "wait For Bytes Written (-1)"
d) when using a serial port sniffer the data is successfully written.
The issue I have is that I don't receive any data, and no data is being emitted from the other device. When using qt terminal writing the same " #002s " produces the correct response.
Any ideas?
many thanks.
#include "test_serial.h"
#include "ui_test_serial.h"
#include <QtSerialPort/QtSerialPort>
#include <QDebug>
QSerialPort *serial;
Test_Serial::Test_Serial(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Test_Serial)
{
ui->setupUi(this);
serial = new QSerialPort(this);
connect(serial,SIGNAL(readyRead()),this,SLOT(serialReceived()));
serial->setPortName("COM1");
serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
serial->open(QIODevice::ReadWrite);
if (serial->isOpen() && serial->isWritable())
{
QByteArray input;
input = "#";
serial->write(input);
serial->waitForBytesWritten(-1);
input = "0";
serial->write(input);
serial->waitForBytesWritten(-1);
input = "0";
serial->write(input);
serial->waitForBytesWritten(-1);
input = "2";
serial->write(input);
serial->waitForBytesWritten(-1);
input = "s";
serial->write(input);
serial->waitForBytesWritten(-1);
input = "\r";
serial->write(input);
serial->waitForBytesWritten(-1);
serial->flush();
serial->waitForReadyRead(100);
QByteArray output = serial->readAll();
ui->label_2->setText(output);
}}
Test_Serial::~Test_Serial()
{
delete ui;
serial->close();
}
void Test_Serial::serialReceived()
{
QByteArray output;
output = serial->readAll();
ui->label->setText("output");
}
If you want to write " #002s ", Why not write at once? May be the serial device cant identify the control code when you write each character.
void Test_Serial::writeDataToSerialPort()
{
QByteArray input = QString("#002s").toLocal8Bit();
serial->write(input);
}
And no need for this reading part .
serial->waitForReadyRead(100);
QByteArray output = serial->readAll();
ui->label_2->setText(output);
The Test_Serial::serialReceived will be called any way when you have the response from the serial device.
And you can catch the error on opening the port by using the error signal from QSerialPort
connect(serial,SIGNAL(error(QSerialPort::SerialPortError)),this,SLOT(serialPortError(QSerialPort::SerialPortError)));
void Test_Serial::serialPortError(QSerialPort::SerialPortError error)
{
//Print error etc.
}
The issue ended up being that the readyread flag is only emitted if theirs data to read. However I was sending the data too quickly for the external device to receive it. This meant that some data was lost thus the device never recognised it as a valid command.
This meant that it was still waiting for the message to finish, hence the "IRP_MJ_DEVICE_CONTROL (IOCTL_SERIAL_WAIT_ON_MASK) UP STATUS_CANCELLED COM1" error message upon closing the program. This also explains why their were no error messages with regards to writing data.
This also explains why the same program occasionally managed to read data, and at other times failed, (even without rebuilding the program, just re-running it.) When the data was read, the processor was more loaded, i.e. programs running in the background. This meant that the data was transmitted more slowly and thus the external device could recognise the commands and thus reply.
We are using FTDI serial port CHIP in our hardware. Now we have working code in Linux and we moved to windows 7. We get some weird problems.
The Problem:
We can't write data to Serial Port without running other console application which do this:
serial.setPortName("COM3");
if (serial.open(QIODevice::ReadWrite)) {
bool success = serial.setBaudRate(QSerialPort::Baud9600) &
serial.setStopBits(QSerialPort::OneStop) &
serial.setDataBits(QSerialPort::Data8) &
serial.setParity(QSerialPort::NoParity) &
serial.setFlowControl(QSerialPort::NoFlowControl);
qDebug() << "Connected to usb device: " << (success ? "OK" : "FAIL");
while(true) {
if(serial.waitForReadyRead(-1)) {
QByteArray out = serial.readAll();
for(int i=0; i< out.length(); i++) {
qDebug() << (int) out[i];
}
}
}
serial.close();
So its just loop with read all. Hardware dosen't send anything, so read is just infinity loop. After closing and running our write program it runs correctly.
char* input;
input = new char[size+3];
QByteArray bytearr;
for(int i=0;i<size+2;i++) {
input[i] = (char) package[i];
bytearr.append((unsigned char) package[i]);
}
QString serialPortName = "COM3";
QSerialPort serialPort;
serialPort.setPortName(serialPortName);
serialPort.open(QIODevice::ReadWrite);
serialPort.write(bytearr);
serialPort.flush();
serialPort.close();
After running read everything works, but without read all, it wont work. What are we doing wrong?
Thanks.
We had similar problem in our application with a board with FTDI chip. We tried to write bytes with 19200 baud/sec, had though in real about 1200 baud/sec (seen using oscilloscope). The problem was closing the serial port right after writing a byte. Just waiting using QThread::msleep(5) before closing the port helped. It seems, that the device gets a reset or something during close operation and latest bytes are sent with false baudrate and other parameters.
I found out that the QT serial port SW requires you to process QT events in order to work.
Putting a qApp->processEvents() in the loop before the read made it work for me.
(QT 4.8.5 on Windows-7)