Unsolved Signal receiving problem from Arduino to Qt graphics view
-
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: