Unsolved Signal receiving problem from Arduino to Qt graphics view
-
Hey guys,
I am doing a project that I can get signal from Arduino to Qt graphics view.
My Arduino code:
void setup() { Serial.begin(9600); } void loop() { for ( int x = 94; x <=512; x++){ delay(1000); Serial.print("x = "); Serial.println(x); } }
And my Qt code :
// Arduino serial port connection code QObject::connect(arduino, SIGNAL(readyRead()), this, SLOT(readSerial())); void MainWindow::updateP() { const QByteArray data = arduino->readAll(); int x1 = data.toInt(); //float y1 = data.toFloat(); qDebug() << "couldn't convert" << data << "to float!"; //set scene QBrush redBrush(Qt::red); QPen blackpen(Qt::black); blackpen.setWidth(1); //qDebug() << "x1:" << x1; //qDebug() << "y1:" << y1; ellipse = scene->addEllipse(x1, 100, 10, 10, blackpen, redBrush); return; } void MainWindow::readSerial() { this->updateP(); }
But in my Qt graphics view, it looks like:
graphics viewThere's a very huge gap from x = 94 to the next ellipse. And also many gaps in the middle of processing.
And some messages in Qt:
couldn't convert "= 10" to float! couldn't convert "3\r\n" to float! couldn't convert "x" to float! couldn't convert " = 1" to float! couldn't convert "04\r\n" to float! couldn't convert "x" to float! couldn't convert " = 1" to float! couldn't convert "05\r\n" to float! couldn't convert "x" to float! couldn't convert " = 1" to float! couldn't convert "06\r\n" to float! couldn't convert "x = " to float! couldn't convert "107\r" to float! couldn't convert "\n" to float! couldn't convert "x = " to float! couldn't convert "108\r" to float! couldn't convert "\n" to float! couldn't convert "x = " to float! couldn't convert "109\r" to float! couldn't convert "\n" to float! couldn't convert "x =" to float! couldn't convert " 110" to float! couldn't convert "\r\n" to float! couldn't convert "x =" to float! couldn't convert " 111" to float! couldn't convert "\r\n" to float! couldn't convert "x =" to float! couldn't convert " 112" to float! couldn't convert "\r\n" to float! couldn't convert "x " to float! couldn't convert "= 11" to float! couldn't convert "3\r\n" to float! couldn't convert "x " to float! couldn't convert "= 11" to float! couldn't convert "4\r\n" to float! couldn't convert "x " to float! couldn't convert "= 11" to float! couldn't convert "5\r\n" to float! couldn't convert "x" to float! couldn't convert " = 1" to float! couldn't convert "16\r\n" to float! couldn't convert "x" to float! couldn't convert " = 1" to float! couldn't convert "17\r\n" to float! couldn't convert "x" to float! couldn't convert " = 1" to float! couldn't convert "18\r\n" to float! couldn't convert "x = " to float! couldn't convert "119\r" to float! couldn't convert "\n" to float! couldn't convert "x = " to float! couldn't convert "120\r" to float! couldn't convert "\n" to float! couldn't convert "x = " to float! couldn't convert "121\r" to float! couldn't convert "\n" to float! couldn't convert "x =" to float! couldn't convert " 122" to float! couldn't convert "\r\n" to float! couldn't convert "x =" to float! couldn't convert " 123" to float! couldn't convert "\r\n" to float! couldn't convert "x =" to float! couldn't convert " 124" to float! couldn't convert "\r\n" to float! couldn't convert "x " to float! couldn't convert "= 12" to float! couldn't convert "5\r\n" to float! couldn't convert "x " to float! couldn't convert "= 12" to float!
and so on....
I am wondering if this is the Arduino Uno's clock speed problem???
Is there any suggestion for this problem?Thank you all so much.
-
Hi,
Looks like you are trying to convert many things that are not numbers. e.g.
= 10
from your message list. -
As I said in my reply to you other post and @SGaist has said in the previous reply, you need to process the output from the Arduino/Serial Port.
toInt()
won't pick numbers out of the string and ignore the rest, you have to parse out the numbers and then convert it. YourupdateP()
method might look something like the following:void MainWindow::updateP(){ const QByteArray data = arduino->readAll(); QTextStream stream(data); QString line; while(stream.readLineInto(&line)){ line.replace(" ","").replace("\n",""); if(line.length()<=0) continue; QStringList bits=line.split("="); if(bits[0]!="x") continue; bool ok; int x=bits[1].toInt(&ok); if(!ok) continue; // Your drawing code here... } }
-
Also, your code is affected by a problem shared by many others:
readAll()
see https://forum.qt.io/topic/70620/wierd-behavior-with-qserialport-readline/3 -
@jazzycamel I would have used a regular expression to parse the output to be honest...
-
I agree a regular expression would be better, I was just trying to make it simpler by using a step-by-step approach to parsing :)
The regular expression version might look like the following:
void MainWindow::updateP(){ const QByteArray data = arduino->readAll(); QTextStream stream(data); QString line; while(stream.readLineInto(&line)){ QRegularExpression re("x = ([0-9]{2,3})"); QRegularExpressionMatch match=re.match(line); if(!match.hasMatch()) continue; QString matched=match.captured(1); bool ok; int x=matched.toInt(&ok); if(!ok) continue; // Your drawing code here... } }
-
Hi @SGaist
I tried to convert the values of X from 94 to 512 and put ellipses on these positions by values of X. So it supposed to be a line if it works fine. Any suggestion??
Hi @VRonin ,
Thank you so much for your information. Sorry I am a very new on Qt. I understand the problem is the "readAll()" command but I don't know what else can I change. Is the readFortune the solution??
Hi @jazzycamel,
Thank you so much for your help. I tried the first solution but it didn't work. It shows a message that my Qt.exe has stopped working. The code is:
void MainWindow::updateP(){ const QByteArray data = arduino->readAll(); QTextStream stream(data); QString line; while(stream.readLineInto(&line)){ line.replace(" ","").replace("\n",""); if(line.length()<=0) continue; QStringList bits=line.split("="); if(bits[0]!="x") continue; bool ok; int x=bits[1].toInt(&ok); if(!ok) continue; //set scene QBrush redBrush(Qt::red); QPen blackpen(Qt::black); blackpen.setWidth(1); //qDebug() << "x1:" << x1; //qDebug() << "y1:" << y1; ellipse = scene->addEllipse(x, 100, 10, 10, blackpen, redBrush); } } void MainWindow::readSerial() { this->updateP(); }
Then I tried the second solution, the QRegularExpression:
void MainWindow::updateP() { const QByteArray data = arduino->readAll(); QTextStream stream(data); QString line; while(stream.readLineInto(&line)){ QRegularExpression re("x = ([0-9]{2,3})"); QRegularExpressionMatch match=re.match(line); if(!match.hasMatch()) continue; QString matched=match.captured(1); bool ok; int x=matched.toInt(&ok); if(!ok) continue; //set scene QBrush redBrush(Qt::red); QPen blackpen(Qt::black); blackpen.setWidth(1); //qDebug() << "x1:" << x1; //qDebug() << "y1:" << y1; ellipse = scene->addEllipse(x, 100, 10, 10, blackpen, redBrush); } } void MainWindow::readSerial() { this->updateP(); }
My Arduino code is the same:
void setup() { Serial.begin(9600); } void loop() { for ( int x = 94; x <=512; x++){ delay(100); Serial.print("x = "); Serial.println(x); } }
Thank you guys for the answers. It's really helpful, but my project is still not working, please give me more information and solution... I appreciate it.
-
Ok, I don't know why your program isn't working or why its hanging. The following is a fully working example that I've tested with an Arduino Uno running your code connected to my MacBook Pro running the following (compiled with Qt5.6):
QT += core gui serialport greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = Forum2 TEMPLATE = app SOURCES += main.cpp\ mainwindow.cpp HEADERS += mainwindow.h
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QSerialPort; class QGraphicsScene; class QGraphicsView; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void updateP(); private: QGraphicsScene *scene; QGraphicsView *view; QSerialPort *arduino; bool open; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include <QRegularExpression> #include <QTextStream> #include <QDebug> #include <QtSerialPort> #include <QGraphicsScene> #include <QGraphicsView> #include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), open(false) { this->resize(1024,768); scene=new QGraphicsScene(this); scene->setSceneRect(0,0,1024,768); view=new QGraphicsView(scene, this); setCentralWidget(view); arduino=new QSerialPort("/dev/ttys002"); arduino->setBaudRate(QSerialPort::Baud9600); connect(arduino, SIGNAL(readyRead()), this, SLOT(updateP())); if(!arduino->open(QSerialPort::ReadOnly)){ qDebug() << "Failed to open serial port!"; return; } open=true; } MainWindow::~MainWindow(){} void MainWindow::updateP(){ if(!open) return; QTextStream stream(arduino); QString line; while(stream.readLineInto(&line)){ QRegularExpression re("x = ([0-9]{2,3})"); QRegularExpressionMatch match=re.match(line); if(!match.hasMatch()) continue; QString matched=match.captured(1); bool ok; int x=matched.toInt(&ok); if(!ok) continue; QBrush redBrush(Qt::red); QPen blackPen(Qt::black, 1.); scene->addEllipse(x, 768/2., 20, 20, blackPen, redBrush); } }
main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
You will need to change the name of the serial/COM port being passed to
QSerialPort
in line 20 of mainwindow.cpp.I hope this gets you up and running :)
-
Hi @jazzycamel ,
Your every answer is really helpful. Thank you so much for this. I copied the exactly the same code you provide and change the serial port.
My graphicsView seems likeCan you take a screen shot on your graphicsView screen for me? And any suggestion?? I think the graphicsView should be the line which is connected by the ellipses from x = 94 to x = 512. Thank you so much.
-
Probably a timing issue. Try putting a longer delay between the
Serial.println()
in the Arduino code and see if it improves. Mine looks like the following: -
Hi @jazzycamel
I tried to extend my delay time but it didn't work. And I thought it's about the data rate problem. So I changed the Serial.begin from 9600 to 115200. And it works fine. Even my original code is fine:)
But here are some questions, why there are so many red ellipse running in the same time? Despite I set it as "for" loop. But is the second ellipse supposed to be set after first ellipse run to the end??
And about the code you provided:
QRegularExpression re("x = ([0-9]{2,3})");
the class information says is a pattern, but why [0-9]{2,3}?
Thank you so much for your help:)
-
The fact that it's not a continuing series of data is probably due to the fact that you read half lines that are skipped by
if(!match.hasMatch()) continue;
. This is the problem with readAll I was talking about.try replacing
if(!match.hasMatch()) continue;
with
if(!match.hasMatch()){ qDebug() << "Row Skipped"; continue; }
and see how many get skipped
QRegularExpression uses perl regular expression. there are whole books written on this sintax, a quick reference can be found here http://www.regular-expressions.info/refquick.html
"x = ([0-9]{2,3})"
means "x = " followed by 2 or 3 digits, the parethesis are used to capture part of the text that is then retrieved bymatch.captured(1);
-
There are so many red ellipses because you draw a new one for each value of
x
rather than moving the original one. Try adding the ellipse once in the MainWindow constructor and then callingellipse->setPos(x,10);
inMainWindow::updateP()
.As @VRonin points out (and as I've said in my posts), this is a very basic, idealised example of how to read and process data from an asynchronous stream. You should probably add available data to a buffer each time
readyRead()
is emitted and then process it such that partially received data is not lost/thrown away.Below is a slightly more complex version of above (still not complete!) and here is a video of it working (I'm sending a Sine function from the Arduino):
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QSerialPort; class QGraphicsScene; class QGraphicsView; class QGraphicsEllipseItem; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void updateP(); private: QGraphicsScene *scene; QGraphicsView *view; QGraphicsEllipseItem *ellipse; QSerialPort *arduino; bool open; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include <QRegularExpression> #include <QTextStream> #include <QDebug> #include <QtSerialPort> #include <QGraphicsScene> #include <QGraphicsView> #include <QGraphicsEllipseItem> #include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), open(false) { this->resize(1024,768); scene=new QGraphicsScene(this); scene->setSceneRect(0,0,1024,768); view=new QGraphicsView(scene, this); setCentralWidget(view); arduino=new QSerialPort("/dev/ttys005"); arduino->setBaudRate(QSerialPort::Baud9600); connect(arduino, SIGNAL(readyRead()), this, SLOT(updateP())); if(!arduino->open(QSerialPort::ReadOnly)){ qDebug() << "Failed to open serial port!"; return; } open=true; QBrush redBrush(Qt::red); QPen blackPen(Qt::black, 1.); ellipse=scene->addEllipse(0, 768/2., 20, 20, blackPen, redBrush); } MainWindow::~MainWindow(){} void MainWindow::updateP(){ if(!open) return; QTextStream stream(arduino); QString line; while(stream.readLineInto(&line)){ QRegularExpression re("x = ([0-9]{2,3})"); QRegularExpressionMatchIterator i=re.globalMatch(line); while(i.hasNext()){ QRegularExpressionMatch match=i.next(); if(!match.hasMatch()){ qDebug() << "No match!"; continue; } QString matched=match.captured(1); bool ok=false; int x=matched.toInt(&ok); if(!ok){ qDebug() << "Could not convert \"" << matched <<"\" to an integer!"; continue; } ellipse->setPos(x,0.); } } }
-
@jazzycamel and @VRonin
Thank you guys so much for your help. Your answer is extremely helpful for novice:)
I am wondering if I could control both X and Y in the same time, my Arduino code is :
void setup() { Serial.begin(115200); } void loop() { int x1 = 10; int y1 = 15; int y = y1; while (y<=100) { for ( int x = x1; x <= 100; x++) { delay(100); Serial.print("x = "); Serial.println(x); } y += 10; Serial.print("y = "); Serial.println(y); for (int x = 100; x >= x1; x--) { delay(100); Serial.print("x = "); Serial.println(x); } y += 10; Serial.print("y = "); Serial.println(y); } }
The graphicsView is supposed to be like
How can I change the code? I think the readAll should be replaced by read x and y data separately, and the QRegularExpression also need to include y.
const QByteArray data = arduino->readAll(); QTextStream stream(data); QString line; while(stream.readLineInto(&line)){ QRegularExpression re("x = ([0-9]{2,3})"); QRegularExpressionMatch match=re.match(line); if(!match.hasMatch()){ qDebug()<<"ROW Skipped"; continue; } QString matched=match.captured(1); bool ok; int x=matched.toInt(&ok); int y=matched.toInt(&ok); if(!ok) continue; //set scene QBrush redBrush(Qt::red); QPen blackpen(Qt::black); blackpen.setWidth(1); ellipse = scene->addEllipse(x, y, 10, 10, blackpen, redBrush);
I've tried this code and it will update x for x and y since I read all signal. Is there any suggestion? Thank you so much:)
-
Very doable, you just need to extend the regular expression to capture the letter before the
=
as well as the value that follows, like so:QRegularExpression re("([x,y]) = ([0-9]{2,3})");
See the example code below and this video of the output.
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QSerialPort; class QGraphicsScene; class QGraphicsView; class QGraphicsEllipseItem; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void updateP(); private: QGraphicsScene *scene; QGraphicsView *view; QGraphicsEllipseItem *ellipse; QSerialPort *arduino; bool open; int x,y; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include <QRegularExpression> #include <QTextStream> #include <QDebug> #include <QtSerialPort> #include <QGraphicsScene> #include <QGraphicsView> #include <QGraphicsEllipseItem> #include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), open(false), x(0), y(0) { this->resize(1024,768); scene=new QGraphicsScene(this); scene->setSceneRect(0,0,1024,768); view=new QGraphicsView(scene, this); setCentralWidget(view); arduino=new QSerialPort("/dev/ttys003"); arduino->setBaudRate(QSerialPort::Baud9600); connect(arduino, SIGNAL(readyRead()), this, SLOT(updateP())); if(!arduino->open(QSerialPort::ReadOnly)){ qDebug() << "Failed to open serial port!"; return; } open=true; QBrush redBrush(Qt::red); QPen blackPen(Qt::black, 1.); ellipse=scene->addEllipse(0, 0, 20, 20, blackPen, redBrush); } MainWindow::~MainWindow(){} void MainWindow::updateP(){ if(!open) return; QTextStream stream(arduino); QString line; while(stream.readLineInto(&line)){ QRegularExpression re("([x,y]) = ([0-9]{2,3})"); QRegularExpressionMatchIterator i=re.globalMatch(line); while(i.hasNext()){ QRegularExpressionMatch match=i.next(); if(!match.hasMatch()){ qDebug() << "No match!"; continue; } QString xory=match.captured(1); QString matched=match.captured(2); bool ok=false; if(xory=="x") x=matched.toInt(&ok); else if(xory=="y") y=matched.toInt(&ok); else continue; if(!ok){ qDebug() << "Could not convert \"" << matched <<"\" to an integer!"; continue; } ellipse->setPos(x,y); } } }
-
Hi @jazzycamel ,
Thanks for your answer, these are all really helpful.
I have tried the code your provide on the potentialmeter signal received, it doesn't work, you have any suggestion??My code:
mainwindow.h#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QGraphicsEllipseItem> class QSerialPort; class QGraphicsScene; class QGraphicsView; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void updateP(); void readSerial(); private: QGraphicsScene *scene; QGraphicsView *view; QSerialPort *arduino; QGraphicsEllipseItem *ellipse; bool open; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include <QRegularExpression> #include <QTextStream> #include <QDebug> #include <QtSerialPort> #include <QGraphicsScene> #include <QGraphicsView> #include <QGraphicsEllipseItem> #include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), open(false) { this->resize(1024,768); scene=new QGraphicsScene(this); scene->setSceneRect(0,0,1024,768); view=new QGraphicsView(scene, this); setCentralWidget(view); arduino=new QSerialPort("COM5"); arduino->setBaudRate(QSerialPort::Baud115200); connect(arduino, SIGNAL(readyRead()), this, SLOT(updateP())); if(!arduino->open(QSerialPort::ReadWrite)){ qDebug() << "Failed to open serial port!"; return; } open=true; } MainWindow::~MainWindow(){} void MainWindow::updateP(){ if(!open) return; QTextStream stream(arduino); QString line; while(stream.readLineInto(&line)){ QRegularExpression re("x = ([0-9]{2,3})"); QRegularExpressionMatch match=re.match(line); if(!match.hasMatch()) continue; QString matched=match.captured(1); bool ok; float x=matched.toFloat(&ok); if(!ok) continue; QBrush redBrush(Qt::red); QPen blackPen(Qt::black, 1.); scene->addEllipse(x, 300, 20, 20, blackPen, redBrush); } } void MainWindow::readSerial() { this->updateP(); }
But this code is perfectly working on for loop"x from 0 to 512".
My Arduino code for potentialmeter signal:
void setup() {: Serial.begin(115200); } void loop() { float x = analogRead (A0); Serial.println(x); delay(5); }
And here is an extra question, how can I write the code only for receiving the value I want to? For example, I have different variable in Arduino x1, x2, y1, y2. But I only want to get the signal of x1 and y1, how can I modify the code?
Thank you so much:)
-
You've changed your Arduino code. You used to print
x = 96
for example, but now you are only printing96
. The regular expression expects the first format. Also, the regular expression works for values 10 - 999, the range ofanalogRead()
is 0-1023. If you want to use the current (value only) format for the full range ofanalogRead()
then I suggest you change the regular expressions as follows:QRegularExpression re("([0-9]{1,4})");
And then, after converting the matched string to an integer, check if its in the desired range:
bool ok=false; int x=matched.toInt(&ok); if(!ok){ qDebug() << "Could not convert \"" << matched <<"\" to an integer!"; continue; } if(x<0 || x>1023){ qDebug() << "Value of x (" << x << ") out of range!"; continue; }
-
Thank you so much for your help, it totally works!
Here are few question:1.What if I would like to send the decimal numbers to Qt and do some calculation, what should I modify the codes?
Say, I would like to send x & y from 1.34 to 4.96 to Qt. I change the code like:void MainWindow::updateP(){ if(!open) return; QTextStream stream(arduino); QString line; while(stream.readLineInto(&line)){ QRegularExpression re("([x,y]) = ([0-9]{-1,4})"); QRegularExpressionMatchIterator i=re.globalMatch(line); while(i.hasNext()){ QRegularExpressionMatch match=i.next(); if(!match.hasMatch()){ qDebug() << "No match!"; continue; } QString xory=match.captured(1); QString matched=match.captured(2); bool ok=false; if(xory=="x") x=matched.toFloat(&ok); else if(xory=="y") y=matched.toFloat(&ok); else continue; if(!ok){ qDebug() << "Could not convert \"" << matched <<"\" to an integer!"; continue; } QBrush redBrush(Qt::red); QPen blackPen(Qt::black, 1.); scene->addEllipse(x*102, y*102, 10, 10, blackPen, redBrush); } } }
- my graphicsView still set weird ellipse on a unset position:
Like this
The ellipses on the left side, I didn't set the position on that but there are still some ellipses, I already set my BaudRate to 115200 and it's the best I could get so far...
The ellipses on the top side of the graphics view is not set, I don't know why there are so many signals to set the ellipses. Please give me some suggestion here.
- If I want to close Arduino reading by click a button, what kind of command should I add in my button_clicked?
void MainWindow::on_Reset_btn_clicked() { }
Any suggestion?
Thank you so much!!:) - my graphicsView still set weird ellipse on a unset position: