Forum: Mikrocontroller und Digitale Elektronik Daten vom Arduino in QT einlesen


von Raphael (Gast)


Lesenswert?

Hallo

Ich habe schon ein wenig hier im Forum gestöbert und war immer sehr 
beeindruckt von der Hilfeleistung die hier auf die Beine gestellt wird. 
Ich habe auch schon gelesen, dass einige von euch mit QT Erfahrung 
haben, ebenso wie mit dem Arduino. Ich brauche von beidem etwas.

Mir geht es darum, dass ich Daten vom Arduino nach QT bekommen will. 
Genauer gesagt habe ich eine ganze Reihe von Sensoren die Temperaturen 
messen, diese wiederum lese ich momentan vom Display ab und trage sie in 
Tabellen ein, diese verwandle ich mit QT und dem QT plotting widget in 
eine Grafik. Nun würde ich gerne das Auslesen automatisieren.
Dazu habe ich mir das QextSerialPort Projekt versucht zu eigen zu 
machen. Ich habe mir also das latest stable 1.2 runtergeladen 
(http://docs.qextserialport.googlecode.com/git/1.2/index.html) und 
versuchte es so wie im Unterpunkt: Usage(1): Source Code Only 
einzubauen. Das hat aber glaube ich nicht ganz so gut hingehaut wie die 
Seite das dort so sagt ...

So sieht meine *.proj Datei aus:
1
#-------------------------------------------------
2
#
3
# Project created by QtCreator 2013-02-19T15:02:56
4
#
5
#-------------------------------------------------
6
7
QT       += core gui
8
9
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
10
11
TARGET = sensor_auslesen
12
TEMPLATE = app
13
14
15
SOURCES += main.cpp\
16
        mainwindow.cpp
17
18
HEADERS  += mainwindow.h
19
20
FORMS    += mainwindow.ui
21
include(qextserialport.pri)

So sieht meine mainwindow.cpp aus:
1
#include "mainwindow.h"
2
#include "ui_mainwindow.h"
3
#include "QextSerialPort.h"
4
5
MainWindow::MainWindow(QWidget *parent) :
6
    QMainWindow(parent),
7
    ui(new Ui::MainWindow)
8
{
9
    ui->setupUi(this);
10
}
11
12
MainWindow::~MainWindow()
13
{
14
    delete ui;
15
}
16
17
void MainWindow::button_WERT_pressed(){
18
19
    // Der Testfloatwert
20
    float f_sensorwert = 20.0;
21
22
    // Umwandlung vom Typ Float auf String für die Ausgabe
23
    QString qs_sensorwert = QString::number(f_sensorwert);
24
25
26
    QextSerialPort *port = new QextSerialPort("COM3");
27
    connect(port, SIGNAL(readyRead()), myClass, SLOT(onDataAvailable()));
28
    port->open();
29
30
31
    // Wert an das Ausgabelabel weitergeben
32
    ui->sensorwert->setText(qs_sensorwert);
33
}
34
void MyClass::onDataAvailable()
35
{
36
    QByteArray data = port->readAll();
37
    processNewData(usbdata);
38
}

Ich wollte das mal kompilieren, aber das macht der Compiler nicht mit.

Der Forenbeitrag hier: Beitrag "Daten mittels QextSerialPort senden"
hat außerdem noch die ganzen Einstellungen für Baudrate usw mit drinnen 
unter port Einstellungen. Dahin komme ich aber gar nicht, mein Kompiler 
sagt:
1
..\sensor_auslesen\mainwindow.cpp: In member function 'void MainWindow::button_WERT_pressed()':
2
..\sensor_auslesen\mainwindow.cpp:27:40: error: 'myClass' was not declared in this scope
3
..\sensor_auslesen\mainwindow.cpp:28:16: error: no matching function for call to 'QextSerialPort::open()'
4
..\sensor_auslesen\mainwindow.cpp:28:16: note: candidate is:
5
In file included from ..\sensor_auslesen\mainwindow.cpp:3:0:
6
..\sensor_auslesen\QextSerialPort.h:196:10: note: virtual bool QextSerialPort::open(QIODevice::OpenMode)
7
..\sensor_auslesen\QextSerialPort.h:196:10: note:   candidate expects 1 argument, 0 provided
8
..\sensor_auslesen\mainwindow.cpp: At global scope:
9
..\sensor_auslesen\mainwindow.cpp:34:6: error: 'MyClass' has not been declared
10
..\sensor_auslesen\mainwindow.cpp: In function 'void onDataAvailable()':
11
..\sensor_auslesen\mainwindow.cpp:36:23: error: 'port' was not declared in this scope
12
..\sensor_auslesen\mainwindow.cpp:37:20: error: 'usbdata' was not declared in this scope
13
..\sensor_auslesen\mainwindow.cpp:37:27: error: 'processNewData' was not declared in this scope
14
..\sensor_auslesen\mainwindow.cpp:36:16: warning: unused variable 'data' [-Wunused-variable]
Nun macht mich ein wenig stutzig:
1
 ...\sensor_auslesen\mainwindow.cpp:27: Fehler:'myClass' was not declared in this scope

Habe ich das aus dem Example mit dem Code falsch verstanden eventuell? 
:)

von Christian (Gast)


Lesenswert?

Du definierst MyClass und verwendest myClass

von Karl H. (kbuchegg)


Lesenswert?

>    connect(port, SIGNAL(readyRead()), myClass, SLOT(onDataAvailable()));

wer oder was ist 'myclass'?

Üblicherweise connected eine Member Funktion die Signale an 
Member-Funktionen derselben Klasse (also MainWidow). Du willst dann 
daher das Signal an einen Slot von this geroutet haben

    connect(port, SIGNAL(readyRead()), this, SLOT(onDataAvailable()));



> void MyClass::onDataAvailable()

Diese Funktion ist daher dann ein Member von MainWindow. Und sie ist 
nicht einfach irgendeine Member-Funktion sondern muss bei den Slots in 
der Klassendeklaration aufgeführt sein, damit sie von connect gefunden 
werden kann.



>     QextSerialPort *port = new QextSerialPort("COM3");
> ...
> void MainWindow::onDataAvailable()
> {
>     QByteArray data = port->readAll();


Allerdings wird sich diese Funktion dann schwer tun, auf eine Variable 
(port) zuzugreifen, die als lokale Variable in einer anderen Member 
Funktion definiert wurde. port muss schon als Member-Variablen der 
Klasse definiert sein, damit sie aus mehreren Funktionen heraus 
benutzbar ist.


> Habe ich das aus dem Example mit dem Code falsch verstanden eventuell?
Ohne jetzt frech sein zu wollen. Aber das Problem scheint nicht beim 
Beispiel zu liegen. Das Problem scheint eher so geartet, dass dir 
elementare Grundlagen der C++ Programmierung fehlen. Da helfen dann auch 
ein paar QT-Beispiele nicht weiter.

von Raphael (Gast)


Lesenswert?

Das habe ich gestern in meiner Müdigkeit nicht mehr richtig gecheckt :) 
Danke jedenfalls für die Hilfe - ich habe leider nur Basics in C++, 
arbeite hauptsächlich mit Fortran (noch lange vor Klassen und Signalen 
...).

Ich habe das nun so gelöst, und das funktioniert auch:
1
port = new QextSerialPort;
2
    port->setPortName(ui->portBox->currentText());
3
    port->setQueryMode(QextSerialPort::EventDriven);
4
    port->setBaudRate(BAUD9600);
5
    port->setFlowControl(FLOW_OFF);
6
    port->setParity(PAR_NONE);
7
    port->setDataBits(DATA_8);
8
    port->setStopBits(STOP_2);
9
10
    connect(port, SIGNAL(readyRead()), this, SLOT(onReadyRead()));

Jetzt kann ich am Port lauschen und krieg auch Daten rein. Soweit so 
gut. Allerdings habe ich nun ein anderes Problem gefunden:

Ich schicke an das GSM Modul das am Arduino ist ein AT - mittels
1
port->write(ui->sendEdit->toPlainText().toLatin1())

Funktioniert alles auch so weit ganz gut, nur bin ich beim Hantieren mit 
Terminal (https://sites.google.com/site/terminalbpp/) draufgekommen, 
dass man unbedingt ein Carriage Return mitschicken muss, damit man zb 
auf AT ein OK bekommt.
Ich habe vermutlich zu naiv gedacht, als ich mir dachte dass ich anfangs 
einfach mein QPlainTextEdit Elemt hernehme und in die erste Zeile das AT 
schreibe und dann mit Enter in die zweite Springe und das dann an das 
GSM Modul schicke. Leuchtet auch irgendwie ein, vermutlich liest der 
einfach eine Zeile und will alles da drin haben ... Ich habe so schwach 
im Hinterkopf, dass ich da ein \r\n unter Windows brauche. Das habe ich 
auch versucht, allerdings habe ich das auch eins zu eins dann so in der 
Ausgabe stehen gehabt. Ich hab nun ein wenig angefangen rumzuprobieren 
und habe es mit QByteArray::fromHex und ähnlichem versucht, bin aber 
nicht zum richtigen Ergebnis gelangt. Wie geht das am einfachsten?

von Karl H. (kbuchegg)


Lesenswert?

Raphael schrieb:

> nicht zum richtigen Ergebnis gelangt. Wie geht das am einfachsten?

  port->write(ui->sendEdit->toPlainText().toLatin1())
  port->write( "\n" );


zu einfach?

von Raphael (Gast)


Lesenswert?

Klingt so einfach, dass es stimmen muss :)

Allerdings habe ich das Problem, dass meine Konsole keine Kommandos 
empfängt scheinbar. Ich tue mir da ein wenig schwer mit dem debuggen, 
weil ich vom GSM Modul ja nur dann eine Antwort kriege, wenn alles 
geklappt hat...

So versuche ich zeichen an meinen COM Port zu schicken (kann ich davon 
ausgehen, dass die Verbindung gut funktioniert, wenn ich die Daten vom 
COM lesen kann, oder kann es sein, dass trotz gutes Lesens die 
Verbindung nicht alle Parameter erfüllt um Schreiben zu können?) - 
schreiben kann ich damit aber nicht :(
1
void Dialog::onSendButtonClicked()
2
{
3
    if (port->isOpen() && !ui->sendEdit->toPlainText().isEmpty()){
4
        port->write(ui->sendEdit->toPlainText().toLatin1());
5
        port->write("\n");
6
    }
7
}

von Raphael (Gast)


Lesenswert?

Achja, warum eigentlich nur ein \n und kein \r\n ?

von Karl H. (kbuchegg)


Lesenswert?

Raphael schrieb:
> Achja, warum eigentlich nur ein \n und kein \r\n ?

Wenns mit \n alleine nicht geht, kannst du ja immer noch ein \r\n 
ausprobieren.

Leider gibt es da historisch gesehen einen Wildwuchs, ob CR alleine 
reicht, oder ob es CR+LF sein muss.


Noch ein Tip:
Es gibt für den PC Programme, die sich "Port Monitor" nennen.
Die protokollieren alles mit, was über die Schnittstelle rein/raus geht.
D.h. da kannst du dir ganz einfach live ansehen, wie andere Software das 
Modem ansteuert, welche Bytes hin und her gehen bzw. was dein Programm 
im Gegensatz dazu tatsächlich macht.

http://www.google.at/#hl=de&gs_rn=4&gs_ri=psy-ab&cp=8&gs_id=u&xhr=t&q=port+monitor&es_nrs=true&pf=p&sclient=psy-ab&oq=port+mon&gs_l=&pbx=1&bav=on.2,or.r_gc.r_pw.r_qf.&bvm=bv.42661473,d.d2k&fp=f5c6d5bbbc0cbbba&biw=1253&bih=642

ist recht nützlich, um Probleme mit Übertragungen auf der seriellen 
Schnittstelle schnell und einfach auf den Grund zu gehen. Denn wie immer 
gilt: Was du programmieren willst und was du programmiert hast, das sind 
oft 2 verschiedene Paar Schuhe :-)

von Raphael (Gast)


Lesenswert?

Heute habe ich vorerst keine Zeit mich darum zu kümmern. Aber das ist 
ein sehr guter Ansatz! Vielen Dank für deine Hilfe erstmal!

von Raphael (Gast)


Lesenswert?

Ich habe nun mit Serial Port Monitor 4.0 den COM Port überwacht und so 
die Ausgaben zwischen Terminal und meinem Code verglichen. Dabei ist mir 
dann aufgefallen, dass das CR von Terminal einem \r entspricht, nicht 
einem \n (nur nebenbei zu erwähnen, falls jemand mal mit google das 
Problem hier findet ...). Nun antwortet mein GSM Modul auch schon brav. 
Ihr seid hier super!

von Raphael (Gast)


Lesenswert?

Ein hoffentlich kleineres Problem habe ich nun aber doch noch 
aufgetrieben :(

Ich habe nun zum Senden einer SMS auch das Problem, dass ich ein STRG-Z 
senden muss. Das habe ich mir einfacher vorgestellt, als es nun 
scheinbar ist... ich versuche es wie folgt, dabei stürzt aber einfach 
nur das GSM Modul ab und beginnt wieder mit
+SIND: 1
+SIND: 10,"SM",1,"FD",1,"LD",1,"MC",1,"RC",1,"ME",1
1
    if(port->isOpen() && !ui->sendEdit->toPlainText().isEmpty())    {
2
       port->write(ui->sendEdit->toPlainText().toLatin1().append(QChar(0x001a)).append("\r"));
wo liegt hier mein Gedankenmurks?

von Raphael (Gast)


Lesenswert?

Ich habe die "\r" auch schon durch "0x000d" ersetzt. Das hat 
funktioniert.

von Karl H. (kbuchegg)


Lesenswert?

Raphael schrieb:

>
1
>     if(port->isOpen() && !ui->sendEdit->toPlainText().isEmpty())    {
2
>        port->write(ui->sendEdit->toPlainText().toLatin1().append(QChar(0x001a)).append("\r"));
3
>
> wo liegt hier mein Gedankenmurks?


Ich hätt mal gesagt, dass du
  zuerst den Ctrl-Z anhängen willst
  dann den \r anhängen willst
  und du erst dann von diesem Gesamtergebnis ein toLatin1 machen willst.


Reihenfolge!
Enteder du arbeitest mit Bytes, die einen ASCII COde haben
Oder du arbeitest mit den QT String Funktionen.
Aber wildes mischen ist ganz schlecht.

Es spricht doch nichts dagegen, dass du 3 Ausgaben anstelle von einer 
machst
1
      port->write( ui->sendEdit->toPlainText().toLatin1() );
2
      port->write( 0x1a );
3
      port->write( '\r' );

Das ist einfach, übersichtlich und man kann mit einem Blick erkennen, 
was eigentlich ausgegeben wird. Und langsamer ist es ganz sicher auch 
nicht. Das ist sowieso I/O begrenzt.
Zu banal?

von Raphael (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Raphael schrieb:
>
>>>     if(port->isOpen() && !ui->sendEdit->toPlainText().isEmpty())    {
>>        port->write(ui->sendEdit->toPlainText().toLatin1().append(QChar(0x001a)) 
.append("\r"));
>> > wo liegt hier mein Gedankenmurks?
>
>
> Ich hätt mal gesagt, dass du
>   zuerst den Ctrl-Z anhängen willst
>   dann den \r anhängen willst
>   und du erst dann von diesem Gesamtergebnis ein toLatin1 machen willst.

Das tue ich doch? Zuerst kommt das append(STRGZ), dann das append(\r)? 
Oder sehe ich das falsch?

von Raphael (Gast)


Lesenswert?

1
if(port->isOpen() && !ui->sendEdit->toPlainText().isEmpty())    {
2
        port->write(ui->sendEdit->toPlainText().toLatin1().append(0x001a));
3
        port->write("\r");

Das funktioniert nun. Ich verstehe nur nicht warum das jetzt anders ist, 
als das oben :(

von Karl H. (kbuchegg)


Lesenswert?

Raphael schrieb:

> Das tue ich doch? Zuerst kommt das append(STRGZ), dann das append(\r)?
> Oder sehe ich das falsch?


Argh.
Jetzt bin ich reingefallen.
Die append gehen ja auf das QByteArray welches von toLatin geliefert 
wird. Ich war irgendwie der Meinung, dass sich das alles wieder auf 
einen QString zurückübersetzt.

Ich würde mal den QChar da rauswerfen. Nicht dass der dafür sorgt, dass 
da ein \0 Byte in die Bytesequenz reinkommt (was fatal wäre, weil write 
da sonst das C-String Ende Zeichen sehen würde)
1
port->write(ui->sendEdit->toPlainText().toLatin1().append((char)0x1a).append("\r"));

von Raphael (Gast)


Lesenswert?

Ok, es funktioniert manchmal. Aber nicht immer. Das dürfte schon vorher 
das Problem gewesen sein. Zwischenfrage nun: ist es wichtig in welcher 
Reihenfolge ich die +SIND Codes retour bekomme? Zb muss 11 der letzte 
sein? Die sind bei mir nicht immer in der selben Reihenfolge, oft habe 
ich 11 als letzten, manchmal auch 4.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.