Qt ReadyRead and AVR devices - c++

I have used Qt with an Arduino UNO to build a data acquisition system. Now I want to migrate that to Qt with an AVR ATmege32u4 microcontroller.
Please look at the code below. I am reading incoming serial data with Qt triggered by the ReadyRead signal, then printing those data to the Qt debug window. This works fine with Qt+UNO, but not Qt+32u4. I rule out any problems with the 32u4 (e.g. bad chip) because the data are printed fine to the serial port monitor in the Arduino IDE.
My questions are:
(1) why does the program work for one AVR device and not the other?
(2) how can I make the program work for the 32u4 microcontroller?
Arduino code (simplified to highlight the problem):
void setup(){
Serial.begin(115200);
while(!Serial){}
}
void loop(){
// wait for incoming serial data from Qt (code not shown)
// record data
// send data back. In this example, send back some text: "abababab"
for(unsigned int i=0;i<4;i++){
Serial.write{0x61}; // "a"
Serial.write{0x62}; // "b"
}
}
Relevant Qt code:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QString>
#include <QDebug>
#include <QStringList>
#include <QtSerialPort>
#include <QDataStream>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
microcontroller = new QSerialPort(this);
// initialize the serial port (baud rate, parity etc.)
connect(microcontroller, &QSerialPort::readyRead, this, &MainWindow::readData);
void MainWindow::readData(){
serialData += microcontroller->readAll();
qDebug() << serialData;
}

You can't open the same COM port twice. If it is open by the Arduino IDE, it won't work on your Qt app.
Also, you should post a MCVE. It's hard to tell what could be wrong if you don't show us the code.

Related

Qt QSerialPort Read Write

I have issue with reading and writing from/to QSerialPort. I read what I have written. Here is some example code that proves that. Maybe I need to write and read from different channels. Code's provided below thx in advance. Provided log of the program with image.
Log of the program here
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QTimer>
#include <QDebug>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QSerialPort *serialPort;
QTimer* sendTimer;
QByteArray receiveDataBuffer;
private slots:
void onReadyRead();
void onSendDataTimeout();
};
#endif // MAINWINDOW_H
MainWindows.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
serialPort(new QSerialPort),
sendTimer(new QTimer)
{
sendTimer->setInterval(1000);
serialPort->setPortName("COM5");
serialPort->setBaudRate(QSerialPort::Baud115200);
serialPort->setParity(QSerialPort::Parity::NoParity);
serialPort->setDataBits(QSerialPort::DataBits::Data8);
serialPort->setStopBits(QSerialPort::StopBits::TwoStop);
serialPort->open(QSerialPort::OpenModeFlag::ReadWrite);
connect(serialPort,&QSerialPort::readyRead,this,&MainWindow::onReadyRead);
connect(sendTimer,&QTimer::timeout,this,&MainWindow::onSendDataTimeout);
sendTimer->start();
}
MainWindow::~MainWindow()
{
}
void MainWindow::onReadyRead()
{
receiveDataBuffer.append(serialPort->readAll());
if (receiveDataBuffer.contains('^') && receiveDataBuffer.contains('$') && receiveDataBuffer.lastIndexOf('$') < receiveDataBuffer.lastIndexOf('^')) {
QByteArray extractedByteArray;
for(int i = receiveDataBuffer.lastIndexOf("$") ; i < receiveDataBuffer.lastIndexOf("^") + 1 ; i++){
extractedByteArray.append(receiveDataBuffer[i]);
}
qDebug()<<"Received:"<<extractedByteArray<<endl;
receiveDataBuffer.clear();
}}
void MainWindow::onSendDataTimeout()
{
qint64 result = serialPort->write("$10,0,123,123^");
bool flush = serialPort->flush();
result != -1 && flush ? qDebug()<<"Data sent"<<endl:qDebug()<<"Failed to send data"<<endl;
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
First of all, try to connect the different signals that are emitted when you read/write that from your port.
For instance to check that the data has been sent properly, according to the documentation you can use the signal QIODevice::bytesWritten:
The signal is emitted every time a payload of data has been written to the device's current write channel. The bytes argument is set to the number of bytes that were written in this payload.
connect(serialPort,&QSerialPort::bytesWritte, this, [](const qint64 bytes) {
qDebug() << "Sent data: " << bytes << " bytes.";
});
To read the data, as you already did, you need to check the QIODevice::readyRead signal:
This signal is emitted once every time new data is available for reading from the device's current read channel. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.
Regarding the problem you described, the first step to do is to ensure that your decive has not been configured to work in echo-mode. To turn it off, you will need to check your device interface.
If this is not the problem, check the available ports by using `QSerialPort::availablePorts and ensure that you are connecting to the right one. For example, to connect to the first port, you can do something like this and check that the configuration is compatible with your device:
auto serialInfo = QSerialPortInfo::availablePorts();
serialPort->setPort(serialInfo[0])
auto ret = serialPort.open(QSerialPort::ReadWrite)
&& serialPort.setBaudRate(QSerialPort::Baud38400)
&& serialPort.setDataBits(QSerialPort::Data8)
&& serialPort.setStopBits(QSerialPort::OneStop)
&& serialPort.setParity(QSerialPort::NoParity);
qDebug() << "Port has been configured properly?: " << ret;
If the problem persist, you can get the native handler and modify its properties by calling QSerailPort::handle().
If the platform is supported and the serial port is open, returns the native serial port handle; otherwise returns -1.
The last option, it does not work in all platforms. Worse scenario, take a look into the official docs of termios. It provides a low-level interface that allows you to enable/disable the echo mode.
struct termios options;
tcgetattr(file, &options);
cfmakeraw(&options);
options.c_lflag &= ~(ECHO | ECHOE); // Add or disable the flags
tcsetattr(file, TCSANOW, &options);
The problem was in
serialPort->setStopBits(QSerialPort::StopBits::TwoStop);
The other device was connected to serial port with QSerialPort::StopBits::OneStop.
So changing the stop bits serialPort->setStopBits(QSerialPort::StopBits::OneStop);
solved the problem and there were no echoes anymore.Thx.

QSerialPort has bytes available but can't read

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.

Communication error between Arduino and Qt using Xbee PRO S1

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.

Listening key events on system level

I need to write an application, which will listen to some key events and after they occurs, application will do something (this isn't important for this question).
The application will run like a deamon - on background (possible on system tray) and waiting for input.
The question is, how can I listen to key events on system level? I prefer some Unix C solution (priority isn't portability to non-Unix system), but if there is some handy Qt class, why don't use it?
EDIT: Isn't there some way, to tell the operating system something like: "Hi! I am here, wake me up on 'some keyboard event'!"?
qxtglobalshortcut is for shortcuts. Qt offers different ways to handle native events. It is for example QWidget::nativeEvent or QAbstractNativeEventFilter.
But if you want to use system API, then you can try my code. It is code which executes inside separate thread and asynchronously invoke method to notify user when event occurs. Ready for copy-paste, but set name of your keyboard.
#include <QApplication>
#include <QDebug>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <termios.h>
#include <signal.h>
#include <QSystemTrayIcon>
#include <thread>
QSystemTrayIcon *tray;
void handler (int sig)
{
qDebug ("nexiting...(%d)n", sig);
exit (0);
}
void perror_exit (char *error)
{
perror (error);
handler (9);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
tray = new QSystemTrayIcon;
QPixmap px(20,20);
px.fill(Qt::green);
tray->setIcon(QIcon(px));
tray->show();
tray->showMessage("hello","hello",QSystemTrayIcon::Information,1000);
//need this to use invokeMthod
qRegisterMetaType<QSystemTrayIcon::MessageIcon>("QSystemTrayIcon::MessageIcon");
std::thread thread([tray]()
{
struct input_event ev[64];
int fd, rd, value, size = sizeof (struct input_event);
char name[256] = "Unknown";
char *device = NULL;
if ((getuid ()) != 0)
qDebug ("You are not root! This may not work...n");
//my keyboard,set name of yours
device = "/dev/input/by-id/usb-SIGMACHIP_USB_Keyboard-event-kbd";
//Open Device
if ((fd = open (device, O_RDONLY)) == -1)
qDebug ("%s is not a vaild device.n", device);
//Print Device Name
ioctl (fd, EVIOCGNAME (sizeof (name)), name);
qDebug ("Reading From : %s (%s)n", device, name);
while (1){
if ((rd = read (fd, ev, size * 64)) < size)
perror_exit ("read()");
value = ev[0].value;
if (value != ' ' && ev[1].value == 1 && ev[1].type == 1){ // Only read the key press event
qDebug ("Code[%d]n", (ev[1].code));
QMetaObject::invokeMethod(tray,"showMessage",Qt::QueuedConnection,Q_ARG(QString,"Was pressed"),Q_ARG(QString,QString::number(ev[1].code)),
Q_ARG(QSystemTrayIcon::MessageIcon,QSystemTrayIcon::Information),Q_ARG(int,500));
}
}
});
qDebug("after thread");
return a.exec();
}
I used code from here, but just changed it to be in Qt manner.
To run programm you must use sudo.
sudo /path/to/exe
#if you want to run it inside qt creator but with sudo
sudo /path/to/qtcreator
It's not Qt (yet) but is somehow connected, there is this class in the Qxt library called qxtglobalshortcut
Here is the link: http://libqxt.bitbucket.org/doc/tip/qxtglobalshortcut.html
Short: You can't do this.
Longer answer: You can, but you have to write an keyboard-driver.
Even with root/Administrator privileges, you need an input-widget which will have OS input focus. (This is to avoid grabbing the input from other input widgets, like password-fields or your chat). - I fully agree ddriver's comment.
If your service has a focused-widget, you can use Widget's text-events or use QObject::installEventFilter
Another point: You can reuse/notify the event within your service but not send to other applications. See notify. If the service handled the keyboard event, no other application will receive the event. And if focus-widget's application already accepted key-event, your service does not get it.
But I do agree: Some OS allow to access the keyboad-devices. (Like Chernobyl's answer). For these, you can implement an own-Keyboard-device driver / handler. Example for Linux embedded is: QWSServer.
Note for users: Using such driver is not save! Please be carefull if you use 3rd party keyboard-driver! For Windows it is highly recomanded only to use Windows-trusted drivers.
Anyway: Implementing an own keyboard-driver can solve Rainbow Tom's issue.

UART to Qt software error - why data always split?

I am tryig to display data I receive via UART on my Qt application.
I send all my data from my microcontroller at once, but my qt application receives it in multiple parts why?
this is what I get: http://imgur.com/kLXRvU5
in stead of: http://imgur.com/h2yNZjl
So every time I receive data my slot function gets called, hence the "data received". But my data is split in two parts. Why please?
my code:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)//, mijnAnimatie(new animatie())
{
serialPort->setPortName("COM13");
serialPort->setBaudRate(QSerialPort::Baud115200);
// serialPort->setDataBits(QSerialPort::Data8);
// serialPort->setParity(QSerialPort::NoParity);
// serialPort->setStopBits(QSerialPort::OneStop);
if (!serialPort->open(QIODevice::ReadWrite))
{
qDebug("some error when opening\n");
}
connect(serialPort, SIGNAL(readyRead()), this, SLOT(updateReceivedData()));
}
void MainWindow::updateReceivedData()
{
qDebug("received data\n");
QString m_readData;
ui->receiveLabel->setText(m_readData);
QByteArray result = serialPort->readAll();
QString command(result); //to convert byte array to string
qDebug()<<result;
ui->receiveLabel->setText(command);
}
Streaming connections, like TCP/IP or serial ports, give you zero guaranties about how the data is cut up into pieces. If you want packets, you have to implement them yourself on top of a streaming connection.