How Do I Make My Program in Qt Continually Send A String to My Arduino?
I suggest you expand on your design somewhat:
- have a repeating
QTimer
with an interval depending on the rate you want to send the string at, and the timer to the function that sends the string - connect the button's pressed signal to start the timer
- connect the button's released signal to stop the timer
Events are sent only once, thus the handlers will be executed only once, if you want to keep on repeating it, you will have to use a timer or some other event driven way. You cannot use a loop as that would block the GUI thread and your application will stop responding.
Sure, you could use the button's auto repeat, and there is the option to adjust the triggering and repeating intervals, but a solution that puts a line between logic and GUI is better. You should really rely on the GUI for storing data or controlling the internal logic. The GUI should only be a front end.
You need more work on the serial port though. If you are going to use it from the GUI thread, you will have to use the non-blocking API. Which will require to extend on your implementation a little bit more. There is a good example on how to achieve that, you only need to modify it to simply enable the sending of further payloads once the previous payload has been successfully sent. In pseudo code:
on button press
start timer
on button release
stop timer
onTimeout
if (can send)
send
can send = false
onBytesWritten
accumulate bytes
if (payload is completed)
can send = true
reset payload byte counter
Of course, you will also have to do some error checking, you can't just expect it to work. The example linked contains basic error handling.
Serial Communication Timeout in QT with Arduino
You are using blocking approach to transmit data via serial port. Unless you are using threads I don't see possibility to send any additional data during execution of previous loop.
BTW: Your program, for example, will block indefinitely if Arduino manages to keep sending something within 10ms periods.
Add couple of QDebug() << "I'm here"; lines to check where your code gets stuck; it is possible that you are blocking somewhere outside code you pasted here. Alternative is to use debugger.
What if previous 'command' you tried to send is still in the buffer ? You'll end up filling output buffer. Check with QDebug how many bytes are in output buffer before writing more data to it. Buffer should be empty. (qint64 QIODevice::bytesToWrite() const).
Connect Arduino with Qt
Problem has been solved:
Qt:
if (serial.isOpen() && serial.isWritable())
{
QByteArray ba("R");
serial.write(ba);
serial.flush();
qDebug() << "data has been send" << endl;
serial.close();
}
Arduino:
int led = 13, avlb = 0;
void setup()
{
Serial.begin(9600);
pinMode(led, OUTPUT);
Serial.println("started");
}
void loop()
{
if (Serial.available() > 0)
{
Serial.println("available");
Serial.println(Serial.available());
delay(2000);
if(Serial.read())
{
Serial.println("read");
Serial.println(Serial.read());
delay(2000);
}
}
else
{
Serial.println("not available");
delay(1000);
}
}
N.B.: Don't forget to put baudrate in Arduino IDE correctly;
In monitor, 9600 baudrate
Qt reading serial data - working code but needs to be more reliable
I didn't much look into it, but it seems the problem might be related to this line:
if(serialData.length()==2*datalength)
So if you got some extra data you just give up on the whole thing? It is not guaranteed that data will arrive at neatly discrete blocks after all.
You should read in the data if length is greater or equal, read in the specified length and leave the remaining data because it is part of the next block.
It would also explain why your function hangs - if you happen to exceed 2*datalength
the condition is never true.
But even if you fix this, the implementation is kinda naive and not something that can be considered fullproof. There are other things that can go wrong, and you will need to have more descriptive block data so you can figure out what went wrong and how to fix it or skip errors without throwing a wrench in the gears so to speak.
Double way Serial communication between Arduino and Qt 5.7
You have no packetization whatsoever: there are no delimiters between individual chunks of data - other than time passing.
On the Arudino side, instead of
write
, you should useprintln
so that each message is a complete line.On the Qt side, process complete lines. You're not guaranteed to get a complete response from the serial port after
waitForReadyRead
. All that you're guaranteed is that at least 1 byte is available to read. That is the source of your problem. Note how you gotLE
, then sometime later you gotD OFF
immediately followed byLED ON
. You must wait for data until complete line(s) are available.
The following should work on the Qt end of things - also note that you don't need as many includes, and you can use QTextStream
instead of iostream, to lower the number of APIs that you use. Finally, you don't need app.exec
since you write blocking code.
// https://github.com/KubaO/stackoverflown/tree/master/questions/arduino-read-40246601
#include <QtSerialPort>
#include <cstdio>
int main(int argc, char *argv[])
{
QCoreApplication a{argc, argv};
QTextStream in{stdin};
QTextStream out{stdout};
QSerialPort port;
port.setPortName("COM6");
port.setBaudRate(9600);
port.setDataBits(QSerialPort::Data8);
port.setParity(QSerialPort::NoParity);
port.setStopBits(QSerialPort::OneStop);
port.setFlowControl(QSerialPort::NoFlowControl);
if (!port.open(QSerialPort::ReadWrite)) {
out << "Error opening serial port: " << port.errorString() << endl;
return 1;
}
while(true)
{
out << "> ";
auto cmd = in.readLine().toLatin1();
if (cmd.length() < 1)
continue;
port.write(cmd);
while (!port.canReadLine())
port.waitForReadyRead(-1);
while (port.canReadLine())
out << "< " << port.readLine(); // lines are already terminated
}
}
If you wish, you can also easily turn it into a GUI application, it's only a few lines to do so:
#include <QtSerialPort>
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication app{argc, argv};
QWidget ui;
QFormLayout layout{&ui};
QLineEdit portName{"COM6"};
QTextBrowser term;
QLineEdit command;
QPushButton open{"Open"};
layout.addRow("Port", &portName);
layout.addRow(&term);
layout.addRow("Command:", &command);
layout.addRow(&open);
ui.show();
QSerialPort port;
port.setBaudRate(9600);
port.setDataBits(QSerialPort::Data8);
port.setParity(QSerialPort::NoParity);
port.setStopBits(QSerialPort::OneStop);
port.setFlowControl(QSerialPort::NoFlowControl);
QObject::connect(&open, &QPushButton::clicked, &port, [&]{
port.setPortName(portName.text());
if (port.open(QSerialPort::ReadWrite)) return;
term.append(QStringLiteral("* Error opening serial port: %1").arg(port.errorString()));
});
QObject::connect(&command, &QLineEdit::returnPressed, &port, [&]{
term.append(QStringLiteral("> %1").arg(command.text()));
port.write(command.text().toLatin1());
});
QObject::connect(&port, &QIODevice::readyRead, &term, [&]{
if (!port.canReadLine()) return;
while (port.canReadLine())
term.append(QStringLiteral("< %1").arg(QString::fromLatin1(port.readLine())));
});
return app.exec();
}
Add GUI to a QT Project with Arduino
The GUI project is completely unrelated to anything Arduino. The page you linked to shows how to use Qt Creator as an IDE to work on Arduino projects, as a replacement to the Arduino IDE. This has nothing to do with GUIs or Qt, you'd be reusing Qt Creator as a general-purpose IDE that it is.
What you need and want is to create a standard Qt project in Qt Creator and go from there. You can also leverage Qt to "simulate" Arduino code without running real Arduino hardware, see this answer for an example.
Arduino doesn't receive data after reconnecting to USB
OK, so, after hours of debugging I've found what caused the problem.
The root of it was that after reconnecting the Arduino, each time I called serial.open in QT, Arduino did a reset (indicated by the blink of the LED) and by the time it was after the bootloader stage and was running the code, the main program had already passed the serial.write QT command without receiving the data.
So, what I did to solve the problem was to just add a Sleep(uint(2000));
after serial.open in order to let Arduino finish booting and then start sending data.
Thank you all for your help and immediate answers!
Related Topics
What Is This Crazy C++11 Syntax ==> Struct:Bar {} Foo {};
What Does Iterator->Second Mean
Why Use #Define Instead of a Variable
What Is the Most Efficient Thread-Safe C++ Logger
Set Breakpoint in C or C++ Code Programmatically for Gdb on Linux
Undefined Behavior in C/C++: I++ + ++I VS ++I + I++
How to Send Keystrokes to a Window
Why It Is Different Between -2147483648 and (Int)-2147483648
Using an Union (Encapsulated in a Struct) to Bypass Conversions for Neon Data Types
C/C++ Inline Assembler with Instructions in String Variables
To Stl or !Stl, That Is the Question